Как проверить, что файл полностью и корректно скачан с FTP-сервера?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе автоматизации обмена данными через FTP-протокол программисты 1С часто сталкиваются с проблемой "битых" файлов. Это ситуация, когда соединение разрывается в процессе передачи, и на диске остается лишь часть данных, при этом система может не выдать явной ошибки. В этой статье мы подробно разберем, как гарантированно определить, что файл скачан полностью и готов к обработке.

Проблема "тихого" обрыва соединения

Прежде чем перейти к решениям, проанализируем ситуацию. Стандартный метод 1С FTPСоединение.Получить() в большинстве случаев корректно обрабатывает ошибки сети. Однако существуют сценарии (особенно на нестабильных каналах или при специфических настройках прокси-серверов), когда файл создается на диске, заполняется частично, и процесс завершается без исключения. Также возможна ситуация, когда внешняя система начала загрузку файла на FTP, а ваша обработка в 1С уже пытается его скачать, пока он еще не дозаписан до конца.

Метод 1: Использование файлов-спутников с контрольной суммой (Хэширование)

Это классический и самый надежный способ, проверенный временем. Суть заключается в том, что рядом с основным файлом (например, data.xml) кладется маленький текстовый файл (например, data.xml.md5), содержащий хэш-сумму основного файла.

Рассмотрим алгоритм работы:

  1. Скачиваем файл с хэшем.
  2. Скачиваем основной файл.
  3. Вычисляем хэш-сумму скачанного файла на стороне 1С.
  4. Сравниваем полученное значение с тем, что написано в файле-спутнике.

Для реализации вычисления хэша в 1С используем объект ХешированиеДанных. Посмотрим на пример кода:


// Пример вычисления хэша MD5 для файла
ХешОбъект = Новый ХешированиеДанных(ХешФункция.MD5);
ХешОбъект.ДобавитьФайл(ПутьКФайлу);
ХешСтрока = НРег(СтрЗаменить(Строка(ХешОбъект.ХешСумма), " ", ""));

Если ХешСтрока = ХешИзФайлаСпутника Тогда
    Сообщить("Файл целостен, можно обрабатывать.");
Иначе
    Сообщить("Ошибка целостности файла!");
КонецЕсли;

Метод 2: Атомарное переименование (Метод временных файлов)

Если мы контролируем и отправку, и получение, то лучшим решением будет использование временных расширений. Проанализируем этот подход подробнее. Отправитель загружает файл под именем document.xml.tmp. Только после того как загрузка на сервер завершена успешно, отправитель выполняет команду переименования в document.xml.

Операция переименования на FTP-сервере является атомарной. Это означает, что если ваша 1С видит в списке файлов document.xml, значит, он гарантированно загружен на сервер полностью. В коде 1С при получении списка файлов через НайтиФайлы() мы просто игнорируем все, что заканчивается на .tmp.

Метод 3: Проверка валидности структуры файла

Выясним причину, по которой файл может считаться "битым" с точки зрения бизнес-логики. Если файл имеет формат XML, ZIP или JSON, недокачанный файл просто не пройдет проверку синтаксиса. Разберем по шагам, как это проверить:

  1. Для ZIP-архивов: Попробуйте открыть архив программно. Оглавление архива всегда находится в самом конце файла. Если файл недокачан, объект ЧтениеZipФайла выдаст ошибку при попытке открытия.
  2. Для XML-файлов: Используйте ЧтениеXML. Если файл обрывается посередине, закрывающий тег не будет найден, и парсер выдаст исключение.

Пример проверки через ZIP:


Попытка
    Архив = Новый ЧтениеZipФайла(ПутьКФайлу);
    Архив.Закрыть();
    // Если ошибок нет, файл скачан полностью
Исключение
    Сообщить("Файл поврежден или не докачан.");
КонецПопытки;

Метод 4: Сравнение размера файла

Рассмотрим подробнее метод сравнения размеров. Это простой способ, но у него есть подводный камень: некоторые FTP-клиенты и файловые системы резервируют место под файл сразу, и его размер может отображаться как целевой, даже если полезные данные еще не переданы. Тем не менее, для большинства случаев это работает.

Алгоритм действий:

  1. Получаем размер файла на сервере с помощью метода FTPСоединение.НайтиФайлы().
  2. Скачиваем файл локально.
  3. Проверяем размер локального файла через объект Файл.

МассивФайлов = ФТПСоединение.НайтиФайлы(ПутьНаСервере, "data.dat");
Если МассивФайлов.Количество() > 0 Тогда
    РазмерНаСервере = МассивФайлов[0].Размер();
    ФТПСоединение.Получить(ПутьНаСервере, ЛокальныйПуть);
    
    ЛокальныйФайл = Новый Файл(ЛокальныйПуть);
    Если ЛокальныйФайл.Размер() = РазмерНаСервере Тогда
        // Успех
    КонецЕсли;
КонецЕсли;

Метод 5: Проверка монопольного доступа (Windows-системы)

Проанализируем ситуацию, когда файл скачивается не самой 1С, а сторонним клиентом. Пока процесс записи в файл активен, операционная система часто блокирует его. Мы можем попытаться открыть файл для записи в монопольном режиме. Если файл "занят" другим процессом, значит, загрузка еще продолжается.

Однако помните, что на Linux-системах это поведение отличается: там файл можно переименовать или удалить, даже если он открыт другим процессом для записи. Поэтому данный метод ориентирован в первую очередь на Windows.

Метод 6: Переход на SFTP (SSH File Transfer Protocol)

Если техническая возможность позволяет, разберем вариант замены протокола. Начиная с версии платформы 8.3.16, 1С поддерживает работу с SFTP "из коробки". В отличие от старого FTP, SFTP работает через SSH-туннель и имеет встроенные механизмы контроля целостности пакетов. Это значительно снижает вероятность появления "тихо" недокачанных файлов. Использование объекта SFTPСоединение делает обмен данными более надежным и защищенным.

Дополнительные системные метки (Zone.Identifier)

Выясним, как понять, что файл вообще пришел из внешней сети. При скачивании файлов браузерами или продвинутыми клиентами в Windows к файлу добавляется альтернативный поток данных (ADS) под названием Zone.Identifier. В нем прописывается ZoneId=3, что означает "Internet". Хотя стандартное FTPСоединение 1С обычно не ставит такую метку, это может быть полезно для анализа файлов, скачанных внешними утилитами типа curl или wget, запущенными из 1С.

Резюме и рекомендации

Подводя итог, можно сказать: никогда не полагайтесь на один лишь факт отсутствия ошибки в методе Получить(). Для критически важных данных мы рекомендуем комбинировать методы: использовать временные расширения (.tmp) и обязательно проверять структуру файла (XML/ZIP) или контрольную сумму после завершения загрузки. Это гарантирует, что ваша система не начнет обрабатывать пустые или поврежденные данные, что сохранит целостность информационной базы.

← На главную