Как выгрузить папку со всеми вложенными файлами и подпапками на FTP в 1С?

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

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

Особенности работы с объектом FTPСоединение

Проанализируем ограничения, с которыми разработчик сталкивается при работе со встроенным механизмом 1С:

  1. Метод FTPСоединение.Записать() принимает только два параметра: путь к конкретному локальному файлу-источнику и путь к целевому файлу на сервере. Передать маску или имя каталога напрямую невозможно.
  2. Метод FTPСоединение.СоздатьКаталог() не может создать всю цепочку вложенных папок за один вызов. Если родительского каталога не существует, попытка создать подпапку вызовет исключение. Каталоги необходимо создавать последовательно от корня к конечной вложенности.
  3. Если каталог на сервере уже существует, метод СоздатьКаталог() также возвращает ошибку, поэтому вызов необходимо оборачивать в конструкцию Попытка ... Исключение либо предварительно проверять структуру через FTPСоединение.НайтиФайлы().

Способ 1. Пофайловая выгрузка средствами 1С с рекурсивным обходом

Рассмотрим основной способ выгрузки штатными средствами платформы. Чтобы не писать собственную сложную рекурсивную функцию для чтения файлов на локальном диске, воспользуемся методом НайтиФайлы(), передав третьим параметром значение Истина. Это позволит за один вызов получить плоский массив всех файлов во всех вложенных каталогах.

Разберем по шагам алгоритм пофайловой отправки:

  1. Инициализируем FTPСоединение один раз перед началом цикла. Обязательно указываем параметр ПассивныйРежим = Истина, чтобы исключить блокировку соединения брандмауэрами.
  2. Получаем массив всех файлов локальной папки.
  3. В цикле для каждого файла вычисляем относительный путь, создаем при необходимости недостающие подкаталоги на FTP-сервере и выполняем отправку.

Посмотрим на пример программной реализации:


Процедура ВыгрузитьКаталогНаFTP(ЛокальныйКаталог, FTPСервер, FTPПорт, FTPПользователь, FTPПароль, FTPКаталогПриемник)
    
    // Инициализируем соединение один раз для всей пачки файлов
    FTP = Новый FTPСоединение(FTPСервер, FTPПорт, FTPПользователь, FTPПароль, , Истина, 60);
    
    // Нормализуем пути
    Если Не Прав(ЛокальныйКаталог, 1) = "\" И Не Прав(ЛокальныйКаталог, 1) = "/" Тогда
        ЛокальныйКаталог = ЛокальныйКаталог + "\";
    КонецЕсли;
    
    // Получаем список всех вложенных файлов
    МассивФайлов = НайтиФайлы(ЛокальныйКаталог, "*.*", Истина);
    
    ДлинаКорня = СтрДлина(ЛокальныйКаталог);
    
    Для Каждого ФайлИсточника Из МассивФайлов Цикл
        
        Если ФайлИсточника.ЭтоКаталог() Тогда
            Продолжить;
        КонецЕсли;
        
        // Получаем относительный путь к файлу
        ОтносительныйПуть = Сред(ФайлИсточника.ПолноеИмя, ДлинаКорня + 1);
        ОтносительныйПуть = СтрЗаменить(ОтносительныйПуть, "\", "/");
        
        // Формируем целевой путь на FTP
        ЦелевойПуть = FTPКаталогПриемник + "/" + ОтносительныйПуть;
        ЦелевойПуть = СтрЗаменить(ЦелевойПуть, "//", "/");
        
        // Создаем дерево каталогов на сервере при необходимости
        СоздатьДеревоКаталоговFTP(FTP, ЦелевойПуть);
        
        // Записываем файл на сервер
        FTP.Записать(ФайлИсточника.ПолноеИмя, ЦелевойПуть);
        
    КонецЦикла;
    
КонецПроцедуры

Процедура СоздатьДеревоКаталоговFTP(FTP, ПолныйЦелевойПуть)
    
    ПозицияСлэша = СтрНайти(ПолныйЦелевойПуть, "/", НаправлениеПоиска.СКонца);
    Если ПозицияСлэша = 0 Тогда
        Возврат;
    КонецЕсли;
    
    ПутьККаталогу = Лев(ПолныйЦелевойПуть, ПозицияСлэша - 1);
    ЧастиПути = СтрРазделить(ПутьККаталогу, "/", Ложь);
    
    ТекущийПуть = "";
    Для Каждого Часть Из ЧастиПути Цикл
        Если ПустаяСтрока(Часть) Тогда
            Продолжить;
        КонецЕсли;
        
        ТекущийПуть = ТекущийПуть + "/" + Часть;
        
        Попытка
            FTP.СоздатьКаталог(ТекущийПуть);
        Исключение
            // Если каталог уже существует, платформа вызовет исключение, которое мы перехватываем
        КонецПопытки;
    КонецЦикла;
    
КонецПроцедуры

Способ 2. Упаковка в архив перед отправкой

Если на стороне FTP-сервера есть возможность настроить автоматическую распаковку файлов (например, через скрипты автоматизации или планировщик cron), наиболее быстрым вариантом будет сжатие каталога в единый ZIP-архив средствами объекта ЗаписьZipФайла.

Передача одного большого архива происходит значительно быстрее, чем отправка сотен мелких изображений по отдельности, так как накладные расходы на сетевые команды FTP для каждого файла сводятся к минимуму.

Способ 3. Использование внешних утилит синхронизации

Если задача выполняется периодически и объем передаваемых данных велик, эффективным решением будет делегирование передачи внешним специализированным утилитам через метод ЗапуститьПриложение().

Рассмотрим преимущества такого подхода:

  1. Консольные утилиты (например, WinSCP или cURL) поддерживают автоматическую синхронизацию только изменившихся файлов, что исключает повторную загрузку уже существующих картинок.
  2. Процесс передачи выполняется в отдельном системном потоке и не блокирует сеанс 1С при выполнении на клиенте.
  3. Внешние утилиты автоматически управляют созданием структуры папок на сервере без необходимости ручного разбора путей в коде.

Посмотрим на пример вызова внешней синхронизации через скрипт WinSCP:


Команда = "winscp.com /command ""open ftp://пользователь:пароль@ftp.example.com/"" ""synchronize remote C:\Images /remote/images"" ""exit""";
ЗапуститьПриложение(Команда, , Истина);

Рекомендации по оптимизации процесса

При реализации выгрузки большого количества файлов штатными средствами 1С обратите внимание на следующие правила:

  1. Никогда не пересоздавайте соединение внутри цикла. Создание нового экземпляра FTPСоединение на каждом файле многократно увеличивает время выгрузки из-за постоянной авторизации и согласования портов.
  2. Выносите длительные выгрузки в фоновые задания. Вызов процедур работы с сетью через ФоновыеЗадания.Выполнить() предотвращает зависание пользовательского интерфейса и разрыв сессий по таймауту.
  3. Увеличивайте таймаут соединения. При работе с нестабильными каналами связи или тяжелыми файлами передавайте значение таймаута в конструктор соединения (например, от 60 до 120 секунд).
← На главную