WinSCP из 1С — самый короткий путь, когда файл нужно отправить по SFTP. В платформе есть FTPСоединение для FTP и FTPS, а SSH-протоколов в ней нет. Консольный WinSCP.com умеет SFTP, SCP, FTP и FTPS, запускается одной строкой и возвращает код завершения, по которому 1С понимает, ушёл файл или нет. Ниже — готовый общий модуль: 1С формирует скрипт WinSCP, запускает его через ЗапуститьПриложение с ожиданием, разбирает XML-лог и возвращает список переданных файлов и тексты ошибок. Отдельно разобраны вариант через .NET-сборку WinSCP и отправка файла по FTP из 1С без сторонних программ.
Отправка по SFTP из 1С почти всегда нужна из-за партнёра, у которого нет API, зато есть сервер с папкой для входящих файлов:
Схема одна: 1С формирует файл, сохраняет на диск и отправляет в каталог партнёра — по кнопке или регламентным заданием. Если формат ещё не выбран, сравните SFTP с другими способами обмена данными 1С: папка на сервере проста, но не говорит, принят ли файл.
WinSCP ставится туда, где выполняется серверный код. В клиент-серверном варианте это сервер 1С, и работать WinSCP будет от имени учётной записи службы (обычно USR1CV8). В файловом варианте — на компьютер пользователя. WinSCP.com и WinSCP.exe должны лежать в одном каталоге: .com — консольная обёртка над .exe. Подойдёт и портативная сборка из zip-архива.
От партнёра нужны адрес, порт, логин, пароль или ключ и каталог для загрузки, для SFTP — ещё отпечаток ключа сервера. Проще всего взять его так: один раз подключиться графическим WinSCP, принять ключ и открыть генерацию кода сессии (в английском интерфейсе — Session → Generate Session URL/Code). WinSCP покажет готовую команду open с параметром -hostkey. Сверьте отпечаток с администратором партнёра.
| Протокол | Адрес в open | Порт по умолчанию | Что учесть |
|---|---|---|---|
| SFTP | sftp:// | 22 | нужен -hostkey |
| SCP | scp:// | 22 | нужен -hostkey, встречается реже SFTP |
| FTP | ftp:// | 21 | пароль идёт по сети открытым текстом |
| FTPS явный | ftpes:// | 21 | самоподписанный сертификат — ключ -certificate с отпечатком |
| FTPS неявный | ftps:// | 990 | шифрование с первого пакета, сертификат — как у явного |
Вот что 1С будет генерировать — минимальный скрипт отправки одного файла:
option batch abort
option confirm off
open sftp://exchange:ПАРОЛЬ@sftp.example.ru:22/ -hostkey="ssh-ed25519 255 ОТПЕЧАТОК" -timeout=30
put "C:\Exchange\price.xml" "/in/price/"
exit
option batch abort — на любой вопрос WinSCP отвечает «прервать» и завершается с кодом 1.option confirm off — существующий файл перезаписывается без вопроса. При запуске с /script обе опции и так включены по умолчанию, строки — для явности.open — адрес в виде URL. Спецсимволы логина и пароля (@, :, /, #, %) кодируются как в URL, иначе WinSCP неправильно разберёт адрес. Без кодирования обходятся ключи -username и -password.-hostkey — ожидаемый отпечаток ключа. Без него в пакетном режиме WinSCP к незнакомому серверу не подключится.put — путь на сервере заканчивается слешем: тогда это каталог, и файл сохранит своё имя.Строка запуска:
"C:\Program Files (x86)\WinSCP\WinSCP.com" /ini=nul /script="C:\Temp\send.txt" /log="C:\Temp\send.log" /xmllog="C:\Temp\send.xml"
Ключ /ini=nul запрещает WinSCP читать и сохранять настройки, в том числе кэш ключей серверов, — результат не зависит от учётной записи. /log пишет подробный журнал сеанса для человека, /xmllog — структурированный лог для программы. Коды завершения по документации WinSCP: 0 — все команды выполнены, 1 — какая-то команда прервана ошибкой.
Упрощённо XML-лог выглядит так:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="exchange@sftp.example.ru">
<upload>
<filename value="C:\Exchange\price.xml" />
<destination value="/in/price/price.xml" />
<result success="true" />
</upload>
<upload>
<filename value="C:\Exchange\stock.xml" />
<result success="false">
<message>Cannot create remote file '/in/price/stock.xml'.</message>
<message>Permission denied. Error code: 3</message>
</result>
</upload>
<failure>
<message>Cannot create remote file '/in/price/stock.xml'.</message>
<message>Permission denied. Error code: 3</message>
</failure>
</session>
Модуль ОбменWinSCP с флагом «Сервер», платформа 8.3.6+ (из-за СтрШаблон). Функция ОтправитьФайлы принимает массив файлов и настройки, возвращает признак успеха, код возврата, переданные файлы и ошибки.
// Общий модуль ОбменWinSCP, флаг «Сервер».
// Платформа 8.3.6+ (СтрШаблон), режим совместимости не ниже 8.3.6.
// WinSCP.com и WinSCP.exe лежат в одном каталоге на машине, где выполняется
// серверный код: в клиент-серверном варианте — на сервере 1С,
// в файловом — на компьютере пользователя.
// Возвращает структуру настроек подключения со значениями по умолчанию.
Функция НовыеНастройки() Экспорт
Настройки = Новый Структура;
Настройки.Вставить("ПутьКWinSCP", "C:\Program Files (x86)\WinSCP\WinSCP.com");
Настройки.Вставить("Протокол", "sftp"); // sftp, scp, ftp, ftpes, ftps
Настройки.Вставить("Сервер", "");
Настройки.Вставить("Порт", 22);
Настройки.Вставить("Пользователь", "");
Настройки.Вставить("Пароль", "");
Настройки.Вставить("ОтпечатокКлюча", ""); // для sftp и scp: "ssh-ed25519 255 ..."
Настройки.Вставить("ОтпечатокСертификата", ""); // для ftpes и ftps с самоподписанным сертификатом
Настройки.Вставить("УдаленныйКаталог", "/upload/"); // обязательно со слешем в конце
Настройки.Вставить("Таймаут", 30); // секунд ожидания ответа сервера
Возврат Настройки;
КонецФункции
// Отправляет файлы на сервер одним сеансом WinSCP.
//
// Параметры:
// ЛокальныеФайлы - Массив - полные имена файлов.
// Настройки - Структура - см. НовыеНастройки().
//
// Возвращаемое значение:
// Структура:
// Успешно - Булево.
// КодВозврата - Число - 0 успех, 1 ошибка WinSCP, -1 WinSCP не запустился.
// Переданы - Массив - файлы, для которых в XML-логе success="true".
// Ошибки - Массив - тексты сообщений WinSCP.
// ПутьКЛогу - Строка - журнал сеанса, сохраняется только при ошибке.
//
Функция ОтправитьФайлы(Знач ЛокальныеФайлы, Знач Настройки) Экспорт
Результат = Новый Структура;
Результат.Вставить("Успешно", Ложь);
Результат.Вставить("КодВозврата", -1);
Результат.Вставить("Переданы", Новый Массив);
Результат.Вставить("Ошибки", Новый Массив);
Результат.Вставить("ПутьКЛогу", ПолучитьИмяВременногоФайла("log"));
ИмяСкрипта = ПолучитьИмяВременногоФайла("txt");
ИмяXMLЛога = ПолучитьИмяВременногоФайла("xml");
// ЗаписьТекста в UTF8 пишет BOM: WinSCP правильно прочитает кириллицу в путях.
Скрипт = Новый ЗаписьТекста(ИмяСкрипта, КодировкаТекста.UTF8);
Скрипт.ЗаписатьСтроку("option batch abort");
Скрипт.ЗаписатьСтроку("option confirm off");
Скрипт.ЗаписатьСтроку(КомандаOpen(Настройки));
Для Каждого ИмяФайла Из ЛокальныеФайлы Цикл
Скрипт.ЗаписатьСтроку(СтрШаблон("put ""%1"" ""%2""", ИмяФайла, Настройки.УдаленныйКаталог));
КонецЦикла;
Скрипт.ЗаписатьСтроку("exit");
Скрипт.Закрыть();
СтрокаЗапуска = СтрШаблон("""%1"" /ini=nul /script=""%2"" /log=""%3"" /xmllog=""%4""",
Настройки.ПутьКWinSCP, ИмяСкрипта, Результат.ПутьКЛогу, ИмяXMLЛога);
КодВозврата = -1;
Попытка
// Третий параметр - ждать завершения, четвёртый - получить код возврата.
ЗапуститьПриложение(СтрокаЗапуска, , Истина, КодВозврата);
Исключение
Результат.Ошибки.Добавить("WinSCP не запущен: "
+ КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Результат.КодВозврата = КодВозврата;
ПрочитатьXMLЛог(ИмяXMLЛога, Результат);
Результат.Успешно = (КодВозврата = 0)
И Результат.Ошибки.Количество() = 0
И Результат.Переданы.Количество() = ЛокальныеФайлы.Количество();
// В скрипте пароль открытым текстом - удаляем сразу.
УдалитьВременныйФайл(ИмяСкрипта);
УдалитьВременныйФайл(ИмяXMLЛога);
Если Результат.Успешно Тогда
УдалитьВременныйФайл(Результат.ПутьКЛогу);
Результат.ПутьКЛогу = "";
КонецЕсли;
Возврат Результат;
КонецФункции
// Собирает строку open: адрес в виде URL, таймаут, отпечаток ключа или сертификата.
Функция КомандаOpen(Настройки)
Адрес = СтрШаблон("%1://%2:%3@%4:%5/",
Настройки.Протокол,
КодироватьСтроку(Настройки.Пользователь, СпособКодированияСтроки.КодировкаURL),
КодироватьСтроку(Настройки.Пароль, СпособКодированияСтроки.КодировкаURL),
Настройки.Сервер,
Формат(Настройки.Порт, "ЧГ=0"));
Команда = СтрШаблон("open %1 -timeout=%2", Адрес, Формат(Настройки.Таймаут, "ЧГ=0"));
Если Настройки.Протокол = "sftp" Или Настройки.Протокол = "scp" Тогда
Команда = Команда + СтрШаблон(" -hostkey=""%1""", Настройки.ОтпечатокКлюча);
ИначеЕсли ЗначениеЗаполнено(Настройки.ОтпечатокСертификата) Тогда
Команда = Команда + СтрШаблон(" -certificate=""%1""", Настройки.ОтпечатокСертификата);
КонецЕсли;
Возврат Команда;
КонецФункции
// Читает XML-лог WinSCP: имена переданных файлов и тексты ошибок.
Процедура ПрочитатьXMLЛог(ИмяXMLЛога, Результат)
ФайлЛога = Новый Файл(ИмяXMLЛога);
Если Не ФайлЛога.Существует() Тогда
Возврат;
КонецЕсли;
ВнутриПередачи = Ложь;
ВнутриСообщения = Ложь;
ТекущийФайл = "";
Чтение = Новый ЧтениеXML;
Попытка
Чтение.ОткрытьФайл(ИмяXMLЛога);
Пока Чтение.Прочитать() Цикл
Имя = Чтение.ЛокальноеИмя;
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если Имя = "upload" Или Имя = "download" Тогда
ВнутриПередачи = Истина;
ТекущийФайл = "";
ИначеЕсли Имя = "filename" И ВнутриПередачи Тогда
ТекущийФайл = Чтение.ПолучитьАтрибут("value");
ИначеЕсли Имя = "result" И ВнутриПередачи Тогда
Если Чтение.ПолучитьАтрибут("success") = "true" Тогда
Результат.Переданы.Добавить(ТекущийФайл);
КонецЕсли;
ИначеЕсли Имя = "message" Тогда
ВнутриСообщения = Истина;
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
Если Имя = "upload" Или Имя = "download" Тогда
ВнутриПередачи = Ложь;
ИначеЕсли Имя = "message" Тогда
ВнутриСообщения = Ложь;
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст И ВнутриСообщения Тогда
// failure повторяет сообщения из result операции - дубли не добавляем.
ТекстОшибки = СокрЛП(Чтение.Значение);
Если Результат.Ошибки.Найти(ТекстОшибки) = Неопределено Тогда
Результат.Ошибки.Добавить(ТекстОшибки);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Исключение
// Лог обрывается, если процесс WinSCP был прерван.
Результат.Ошибки.Добавить("XML-лог WinSCP не прочитан: "
+ КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Процедура УдалитьВременныйФайл(ИмяФайла)
Попытка
УдалитьФайлы(ИмяФайла);
Исключение
// Файл мог не создаться - на результат передачи это не влияет.
КонецПопытки;
КонецПроцедуры
ЗапуститьПриложение(СтрокаЗапуска, , Истина, КодВозврата) — третий параметр заставляет 1С ждать завершения WinSCP, в четвёртый приходит код завершения. Без ожидания код не заполняется, а скрипт удаляется раньше, чем WinSCP его прочитает.КодироватьСтроку(..., СпособКодированияСтроки.КодировкаURL) — делает логин и пароль безопасными для URL.КодировкаТекста.UTF8 — WinSCP принимает скрипт в UTF-8 или UTF-16 с BOM; в ANSI испортится кириллица в путях.ПрочитатьXMLЛог — для каждой операции upload или download WinSCP пишет имя файла и result с атрибутом success, тексты ошибок — в элементах message. Элемент failure повторяет сообщения неудачной операции, поэтому дубли отсекаются. Ошибка подключения попадает только в failure — её процедура тоже ловит.Успешно — код возврата, отсутствие ошибок и совпадение числа переданных файлов с числом отправляемых. Код 0 при пустом списке переданных — повод разбираться.// Вызывается из обработчика регламентного задания (общий модуль с флагом «Сервер»).
// НастройкаПодключения - ссылка на элемент справочника, для которого пароль
// записан в безопасное хранилище БСП.
Процедура ВыгрузитьПрайсНаSFTP(НастройкаПодключения, ИмяФайлаПрайса) Экспорт
Настройки = ОбменWinSCP.НовыеНастройки();
Настройки.Сервер = "sftp.example.ru";
Настройки.Пользователь = "exchange";
Настройки.ОтпечатокКлюча = "ssh-ed25519 255 ...";
Настройки.УдаленныйКаталог = "/in/price/";
УстановитьПривилегированныйРежим(Истина);
Настройки.Пароль = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(НастройкаПодключения, "Пароль");
УстановитьПривилегированныйРежим(Ложь);
Файлы = Новый Массив;
Файлы.Добавить(ИмяФайлаПрайса);
Результат = ОбменWinSCP.ОтправитьФайлы(Файлы, Настройки);
Если Не Результат.Успешно Тогда
Комментарий = СтрСоединить(Результат.Ошибки, Символы.ПС)
+ Символы.ПС + "Код возврата: " + Результат.КодВозврата
+ Символы.ПС + "Журнал сеанса: " + Результат.ПутьКЛогу;
ЗаписьЖурналаРегистрации("ОбменSFTP.Выгрузка", УровеньЖурналаРегистрации.Ошибка, , , Комментарий);
ВызватьИсключение "Прайс не отправлен по SFTP, подробности в журнале регистрации";
КонецЕсли;
КонецПроцедуры
Пароль не храните в константе или реквизите — его прочитает любой пользователь с правом просмотра. В конфигурациях на БСП есть безопасное хранилище: ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище и ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища, оба работают в привилегированном режиме. ВызватьИсключение в конце нужен, чтобы задание завершилось с ошибкой и это было видно в списке заданий.
Робот партнёра, который забирает файлы по расписанию, может схватить файл посреди загрузки. Крупные файлы по SFTP WinSCP по умолчанию грузит под временным именем .filepart, но надёжнее переименовать файл явно командой mv:
put "C:\Exchange\price.xml" "/in/price/price_20260919_0300.xml.tmp"
mv "/in/price/price_20260919_0300.xml.tmp" "/in/price/price_20260919_0300.xml"
Договоритесь с партнёром, что файлы *.tmp робот пропускает. Дата и время в имени нужны потому, что часть SFTP-серверов запрещает переименование поверх существующего файла.
Библиотеку WinSCPnet.dll можно зарегистрировать как COM-объект и работать из 1С без временных скриптов: параметры задаются свойствами, ошибка приходит исключением. Регистрация — один раз, с правами администратора, из каталога со сборкой. Разрядность RegAsm должна совпадать с разрядностью процесса 1С — для 64-битного сервера это каталог Framework64, для 32-битного клиента — Framework:
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe WinSCPnet.dll /codebase /tlb:WinSCPnet64.tlb
// Отправка через WinSCP .NET assembly, зарегистрированную для COM.
// Только Windows; разрядность регистрации = разрядность процесса 1С.
Процедура ОтправитьФайлЧерезCOM(ИмяФайла, Настройки) Экспорт
ОпцииСеанса = Новый COMОбъект("WinSCP.SessionOptions");
ОпцииСеанса.Protocol = 0; // 0 - SFTP, 1 - SCP, 2 - FTP
ОпцииСеанса.HostName = Настройки.Сервер;
ОпцииСеанса.PortNumber = Настройки.Порт;
ОпцииСеанса.UserName = Настройки.Пользователь;
ОпцииСеанса.Password = Настройки.Пароль;
ОпцииСеанса.SshHostKeyFingerprint = Настройки.ОтпечатокКлюча;
Сеанс = Новый COMОбъект("WinSCP.Session");
// Нужно, только если WinSCP.exe лежит не рядом с WinSCPnet.dll:
// Сеанс.ExecutablePath = "C:\Program Files (x86)\WinSCP\WinSCP.exe";
Попытка
Сеанс.Open(ОпцииСеанса);
ОпцииПередачи = Новый COMОбъект("WinSCP.TransferOptions");
// Первый параметр - маска: символы * ? [ в имени файла придётся экранировать.
Итог = Сеанс.PutFiles(ИмяФайла, Настройки.УдаленныйКаталог, Ложь, ОпцииПередачи);
Итог.Check(); // исключение, если хотя бы один файл не передан
Исключение
Сеанс.Dispose();
ВызватьИсключение;
КонецПопытки;
Сеанс.Dispose();
КонецПроцедуры
Перечисления .NET из 1С по имени недоступны, поэтому протокол задаётся числом: 0 — SFTP, 1 — SCP, 2 — FTP, 3 — WebDAV, 4 — S3. Dispose вызывайте и в ветке исключения, иначе процесс WinSCP.exe останется висеть на сервере.
| Скрипт и WinSCP.com | .NET-сборка через COM | |
|---|---|---|
| Установка | скопировать WinSCP | плюс регистрация RegAsm с правами администратора |
| Пароль | на время работы лежит во временном файле | только в памяти процесса |
| Ошибки | код возврата и XML-лог | исключение с текстом WinSCP |
| Логика между операциями | всё заранее в одном скрипте | обычный код 1С между вызовами методов |
| Перенос на другой сервер | скопировать каталог | повторить регистрацию |
Для отправки по расписанию хватает скрипта. COM удобнее, когда решение принимается по ходу сеанса: например, скачать только новые файлы.
Если партнёру достаточно обычного FTP или FTPS, сторонняя программа не нужна — в платформе есть FTPСоединение:
// Обычный FTP средствами платформы, без сторонних программ.
Процедура ОтправитьФайлПоFTP(ИмяФайла, Сервер, Пользователь, Пароль) Экспорт
// Параметры: сервер, порт, пользователь, пароль, прокси,
// пассивный режим, таймаут в секундах.
Соединение = Новый FTPСоединение(Сервер, 21, Пользователь, Пароль, , Истина, 30);
Соединение.УстановитьТекущийКаталог("/upload");
Файл = Новый Файл(ИмяФайла);
Соединение.Записать(ИмяФайла, Файл.Имя);
КонецПроцедуры
Шестой параметр включает пассивный режим — без него соединение часто не проходит через NAT и фаерволы. Для FTPS восьмым параметром передаётся Новый ЗащищенноеСоединениеOpenSSL. SFTP этот объект не поддерживает: SFTP работает поверх SSH и с FTP общего имеет только название.
| Сообщение или симптом | Причина | Что сделать |
|---|---|---|
| «Host key wasn't verified!» или «Host key does not match configured key» | не передан -hostkey, опечатка в отпечатке или партнёр сменил ключ сервера | сверить отпечаток с администратором партнёра. -hostkey="*" (принять любой ключ) допустим только для проверки |
| «Authentication failed.» или «Access denied.» | неверный логин или пароль, спецсимволы в пароле не закодированы | войти тем же логином из графического WinSCP |
| «Network error: Connection timed out.» или «Connection refused» | порт закрыт фаерволом, IP сервера 1С не внесён в белый список партнёра | проверять подключение с самого сервера 1С |
| «Upload of file '…' was successful, but error occurred while setting the permissions and/or timestamp.» | сервер не разрешает менять дату файла | добавить к put ключ -nopreservetime |
| «Ошибка при вызове конструктора (COMОбъект) … Недопустимая строка с указанием класса» | сборка не зарегистрирована или зарегистрирована не той разрядности | повторить RegAsm из каталога Framework64 для 64-битного сервера 1С |
КодВозврата = -1 и ошибка «WinSCP не запущен» | неверный путь к WinSCP.com на сервере; код выполняется в безопасном режиме или профиль безопасности запрещает запуск приложений | проверить путь на сервере; для внешней обработки — разрешение в профиле безопасности |
| файл лёг не в тот каталог или под чужим именем | путь на сервере без слеша в конце: WinSCP принял последнюю часть пути за имя файла | заканчивать удалённый каталог символом / |
Средствами платформы — нет, встроенного SFTP-клиента в ней нет. Кроме WinSCP подойдут другие консольные клиенты: sftp из OpenSSH (удобен при входе по ключу) или psftp из PuTTY. Принцип тот же: файл команд, запуск с ожиданием, проверка кода возврата. Если партнёр принимает данные по HTTP, хватит HTTPСоединение.
Вместо put — get, например get -delete "/out/*.xml" "C:\Exchange\in\". Ключ -delete удаляет файл на сервере после успешного скачивания, чтобы не загрузить его повторно. ПрочитатьXMLЛог уже понимает элементы download — остаётся функция-близнец со строкой get.
WinSCP — программа для Windows. На Linux используют штатный sftp в пакетном режиме (sftp -b) с авторизацией по ключу или lftp. Код 1С меняется мало: другой текст скрипта и другая строка запуска, проверка кода возврата та же.
В команду open добавляется -privatekey="путь к ключу", пароль из адреса убирается. WinSCP работает с ключами в формате PuTTY (.ppk), ключ OpenSSH переводится в этот формат программой PuTTYgen из поставки WinSCP. Файл ключа должен быть доступен на чтение учётной записи службы 1С — и больше никому.
Задание выполняется на сервере 1С под учётной записью службы: другой компьютер, другие права, другой выход в интернет. Проверяйте WinSCP с сервера и от имени этой учётной записи, журнал из /log покажет шаг, на котором всё оборвалось.
Если обмен по SFTP нужен в рабочей базе — с расписанием, повторными попытками, уведомлением ответственного и журналом отправок, — а своих рук не хватает, отдайте задачу исполнителю. Подходящие направления собраны в разделе услуг по доработке 1С. Опишите, какие файлы, куда и как часто отправлять, и разместите задачу на площадке: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.