Как на SFTP перебрать и обработать список файлов по одному с помощью WinSCP в 1С?

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

При интеграции 1С с внешними системами часто возникает необходимость обмена данными по защищенному протоколу SFTP (SSH File Transfer Protocol). Стандартный встроенный объект платформы 1С FTPСоединение поддерживает только обычный FTP и его защищенную версию FTPS (FTP over SSL/TLS). Протокол SFTP платформой нативно не поддерживается. В таких случаях классическим решением является использование сторонней бесплатной утилиты WinSCP.

Обычная синхронизация каталогов (команда synchronize) или пакетное скачивание файлов «пачкой» не всегда подходят. Например, если в процессе скачивания большого объема файлов произойдет сбой связи, часть файлов может потеряться или удалиться с сервера без фактического сохранения на локальный диск. Для обеспечения надежности (транзакционности) процесса обмена необходимо перебирать файлы на SFTP-сервере по одному, скачивать каждый файл отдельно и удалять его с сервера только после успешного подтверждения записи на локальный диск. Рассмотрим, как решить эту задачу в 1С двумя способами.

Способ 1. Надежный и современный — использование WinSCP .NET Assembly (COM-объект)

Самый стабильный и профессиональный подход — использование официальной .NET-библиотеки WinSCP. Этот метод позволяет работать с файловой структурой удаленного сервера как с полноценными объектами 1С, избегая создания временных файлов и «черных окон» командной строки. Мы получаем полный контроль над процессом: можем прочитать свойства файла (размер, дата изменения, имя) и обработать каждую ошибку индивидуально.

Шаг 1. Регистрация библиотеки в операционной системе

Для начала необходимо скачать пакет автоматизации WinSCP (содержащий файлы WinSCPnet.dll и winscp.exe) с официального сайта и поместить их в один каталог на сервере 1С. После этого зарегистрируем библиотеку в Windows под правами администратора с помощью утилиты RegAsm.exe:

%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe WinSCPnet.dll /codebase /tlb

Шаг 2. Разработка кода в 1С

Разберем по шагам процесс подключения, получения списка файлов, их скачивания и удаления в цикле:


// 1. Инициализация параметров подключения к SFTP
ПараметрыСессии = Новый COMОбъект("WinSCP.SessionOptions");
ПараметрыСессии.Protocol = 0; // 0 соответствует протоколу SFTP в библиотеке WinSCP
ПараметрыСессии.HostName = "sftp.yourserver.com";
ПараметрыСессии.UserName = "sftp_login";
ПараметрыСессии.Password = "sftp_password";

// Важно: укажите отпечатков SSH-ключа сервера (SshHostKeyFingerprint). 
// Без него библиотека откажется подключаться из соображений безопасности.
ПараметрыСессии.SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"; 

Сессия = Новый COMОбъект("WinSCP.Session");
// Если winscp.exe расположен не в одной папке с DLL, укажите путь к нему явно:
// Сессия.ExecutablePath = "C:\Program Files (x86)\WinSCP\winscp.exe";

Попытка
    // 2. Открытие защищенного соединения
    Сессия.Open(ПараметрыСессии);
    
    КаталогНаSFTP = "/remote/inbox/";
    ЛокальныйКаталог = "C:\1C_Exchange\Inbox\";
    
    // Получаем коллекцию файлов в удаленном каталоге
    ИнформацияОКаталоге = Сессия.ListDirectory(КаталогНаSFTP);
    
    // 3. Поштучный перебор коллекции файлов
    Для Каждого ФайлИзКаталога Из ИнформацияОКаталоге.Files Цикл
        
        // Пропускаем каталоги (включая служебные переходы "." и "..")
        Если ФайлИзКаталога.IsDirectory Тогда
            Продолжить;
        КонецЕсли;
        
        ИмяФайла = ФайлИзКаталога.Name;
        
        // При необходимости фильтруем файлы по маске (например, только XML)
        Если Не Найти(НРег(ИмяФайла), ".xml") > 0 Тогда
            Продолжить;
        КонецЕсли;
        
        ПолныйПутьНаSFTP = КаталогНаSFTP + ИмяФайла;
        ПолныйЛокальныйПуть = ЛокальныйКаталог + ИмяФайла;
        
        // 4. Безопасное скачивание и удаление в транзакции
        Попытка
            // Скачиваем конкретный файл во временный или рабочий локальный каталог
            РезультатПередачи = Сессия.GetFiles(ПолныйПутьНаSFTP, ПолныйЛокальныйПуть);
            
            // Метод Check() генерирует внутреннее исключение 1С, если передача прервалась
            РезультатПередачи.Check(); 
            
            // Файл гарантированно сохранен на диске. Теперь его можно безопасно удалить с SFTP.
            Сессия.RemoveFiles(ПолныйПутьНаSFTP);
            
            Сообщить("Успешно обработан файл: " + ИмяФайла);
            
        Исключение
            // Если произошел сбой при скачивании конкретного файла, 
            // он останется на сервере и будет обработан при следующем сеансе.
            ЗаписьЖурналаРегистрации("Обмен SFTP", УровеньЖурналаРегистрации.Ошибка, , , 
                "Ошибка обработки файла " + ИмяФайла + ": " + ОписаниеОшибки());
        КонецПопытки;
        
    КонецЦикла;

Исключение
    Сообщить("Ошибка подключения или работы с SFTP: " + ОписаниеОшибки());
КонецПопытки;

// 5. Обязательное освобождение занятых ресурсов
Если Сессия.Opened Тогда
    Сессия.Close();
КонецЕсли;
Сессия.Dispose();

Способ 2. Альтернативный (консольный) — запуск через CMD и парсинг текстового файла

Рассмотрим ситуацию, когда у вас нет прав администратора на сервере 1С для регистрации .NET-библиотеки в реестре Windows. В этом случае приходится использовать классический консольный интерфейс WinSCP. Суть метода заключается в генерации временного скрипта команд (файла сценария), его выполнении через WScript.Shell с перенаправлением вывода команды ls в текстовый файл и последующим парсингом этого файла.

Проанализируем пример функции, которая реализует такой подход:


Функция ПолучитьМассивФайловWinSCP(ИмяFTPСервера, ИмяКаталогаСервера, Маска = "*.*", Позиция = 65)
    МассивФайлов = Новый Массив;
    
    // Формируем уникальные имена для временных файлов команд и логов
    ИмяВремФайлаРезультата = КаталогВременныхФайлов() + "winscpls" + Формат(НомерСоединенияИнформационнойБазы(), "ЧГ=") + ".txt";
    Файл = Новый Файл(ИмяВремФайлаРезультата);
    Если Файл.Существует() Тогда
        УдалитьФайлы(ИмяВремФайлаРезультата);
    КонецЕсли;
    
    ИмяВремФайлаКоманды = КаталогВременныхФайлов() + "winscp" + Формат(НомерСоединенияИнформационнойБазы(), "ЧГ=") + ".cmd";
    ДобавитьНомерКФайлуКоманды = Ложь;
    Файл = Новый Файл(ИмяВремФайлаКоманды);
    Если Файл.Существует() Тогда
        Попытка
            УдалитьФайлы(ИмяВремФайлаКоманды);
        Исключение
            ДобавитьНомерКФайлуКоманды = Истина;
        КонецПопытки;
    КонецЕсли;
    
    Если ДобавитьНомерКФайлуКоманды Тогда
        Для сч = 1 По 999 Цикл
            ИмяВремФайлаКоманды = КаталогВременныхФайлов() + "winscp" + Формат(НомерСоединенияИнформационнойБазы(), "ЧГ=") + "_" + СокрЛП(сч) + ".cmd";
            Файл = Новый Файл(ИмяВремФайлаКоманды);
            Если НЕ Файл.Существует() Тогда
                Прервать;
            КонецЕсли;
        КонецЦикла; 
    КонецЕсли;
    
    // Подготовка командного сценария для WinSCP
    Парам = ПреобразоватьВКомандуWinSCP(
                "option batch abort
                |open " + ИмяFTPСервера + "
                |cd " + ИмяКаталогаСервера + "
                |ls
                |exit");
                
    Стр = ПутьКWinSCP + " /command " + Парам;
    Текст = Новый ТекстовыйДокумент;
    Текст.ДобавитьСтроку(Стр);
    Текст.Записать(ИмяВремФайлаКоманды, КодировкаТекста.OEM);
    
    // Выполнение скрипта без открытия черного окна CMD
    WshShell = Новый COMОбъект("WScript.Shell");
    Результат = WshShell.Run("""" + ИмяВремФайлаКоманды + """ > """ + ИмяВремФайлаРезультата + """", 0, 1);
    
    Попытка
        УдалитьФайлы(ИмяВремФайлаКоманды);
    Исключение
    КонецПопытки;
    
    Файл = Новый Файл(ИмяВремФайлаРезультата);
    Если Результат <> 0 Тогда
        Попытка
            Если Файл.Существует() Тогда
                УдалитьФайлы(ИмяВремФайлаРезультата);
            КонецЕсли;
        Исключение
        КонецПопытки;
        Возврат МассивФайлов;
    КонецЕсли;
    
    // Чтение и парсинг лог-файла с результатами команды "ls"
    Если НЕ Файл.Существует() Тогда
        Возврат МассивФайлов;
    КонецЕсли;
    
    Текст = Новый ТекстовыйДокумент;
    Текст.Прочитать(ИмяВремФайлаРезультата, КодировкаТекста.UTF8);
    ВсегоСтрок = Текст.КоличествоСтрок();
    НачалсяСписокФайлов = Ложь;
    
    Для счСтрок = 1 По ВсегоСтрок Цикл
        Стр = Текст.ПолучитьСтроку(счСтрок);
        
        // Ищем строку инициализации списка файлов
        Если Сред(Стр, 55, 4) = "  .." Тогда
            НачалсяСписокФайлов = Истина;
        КонецЕсли;
        
        Если НЕ НачалсяСписокФайлов Тогда
            Продолжить;
        КонецЕсли;
        
        Если СтрДлина(Стр) < Позиция Тогда
            Продолжить;
        ИначеЕсли Лев(Стр, 1) = "D" Тогда // Пропускаем директории
            Продолжить;
        КонецЕсли;
        
        // Вырезаем имя файла по фиксированной стартовой позиции в строке лога
        ИмяФайла = Сред(Стр, Позиция);
        
        // Функция проверки имени файла по заданной маске (например, *.xml)
        Если НЕ ПроверкаСтрокиПоМаске(ИмяФайла, Маска) Тогда
            Продолжить;
        КонецЕсли;
        
        МассивФайлов.Добавить(ИмяФайла);
    КонецЦикла; 
    
    Попытка
        УдалитьФайлы(ИмяВремФайлаРезультата);
    Исключение
    КонецПопытки;
    
    Возврат МассивФайлов;
КонецФункции

Важное предупреждение: Способ с парсингом текстового лога крайне ненадежен и чувствителен к внешним факторам. Если администратор SFTP-сервера обновит его ПО, или вы обновите версию WinSCP, формат вывода утилиты (ширина колонок, формат вывода даты/времени) может измениться. В таком случае жестко заданная переменная Позиция = 65 начнет вырезать неверные части строк, и парсинг полностью сломается. Используйте этот способ только в крайних случаях, когда COM-объекты недоступны.

Сравнение подходов

Подведем итог и проанализируем преимущества использования нативного COM-объекта перед запуском через CMD:

  1. Транзакционность и безопасность данных: При использовании COM-объекта удаление файла с SFTP происходит только после явного вызова метода Check(). При запуске пакетных скриптов CMD невозможно гибко обработать ошибку скачивания конкретного файла внутри пакета — пачка либо скачивается целиком, либо удаляется с сервера с потенциальной потерей данных.
  2. Производительность: COM-объект выполняет все операции внутри оперативной памяти. Нам не нужно создавать, записывать и постоянно удалять временные командные файлы .cmd и логи .txt на жестком диске сервера.
  3. Стабильность структуры: Объектная модель WinSCP гарантирует, что свойства Name и IsDirectory будут возвращать корректные значения независимо от языковых настроек операционной системы или версии самого WinSCP. Вы избавлены от необходимости вычислять символьные позиции в строках.

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

← На главную