При интеграции 1С с внешними системами часто возникает необходимость обмена данными по защищенному протоколу SFTP (SSH File Transfer Protocol). Стандартный встроенный объект платформы 1С FTPСоединение поддерживает только обычный FTP и его защищенную версию FTPS (FTP over SSL/TLS). Протокол SFTP платформой нативно не поддерживается. В таких случаях классическим решением является использование сторонней бесплатной утилиты WinSCP.
Обычная синхронизация каталогов (команда synchronize) или пакетное скачивание файлов «пачкой» не всегда подходят. Например, если в процессе скачивания большого объема файлов произойдет сбой связи, часть файлов может потеряться или удалиться с сервера без фактического сохранения на локальный диск. Для обеспечения надежности (транзакционности) процесса обмена необходимо перебирать файлы на SFTP-сервере по одному, скачивать каждый файл отдельно и удалять его с сервера только после успешного подтверждения записи на локальный диск. Рассмотрим, как решить эту задачу в 1С двумя способами.
Самый стабильный и профессиональный подход — использование официальной .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();
Рассмотрим ситуацию, когда у вас нет прав администратора на сервере 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:
Check(). При запуске пакетных скриптов CMD невозможно гибко обработать ошибку скачивания конкретного файла внутри пакета — пачка либо скачивается целиком, либо удаляется с сервера с потенциальной потерей данных..cmd и логи .txt на жестком диске сервера.Name и IsDirectory будут возвращать корректные значения независимо от языковых настроек операционной системы или версии самого WinSCP. Вы избавлены от необходимости вычислять символьные позиции в строках.Рекомендуется использовать первый способ (COM-объект) как основной стандарт для разработки надежных интеграционных решений в среде 1С:Предприятие.