Выгрузка файла из 1С:Предприятия 8.3 на FTP: объект FTPСоединение от подключения до проверки
Содержание
Файл на FTP сервер из 1С 8.3 записывают встроенным объектом FTPСоединение: создают соединение с адресом, портом, логином и паролем, переходят в нужный каталог и вызывают метод Записать(ЛокальныйПуть, ИмяНаСервере). Код выполняют на сервере 1С (&НаСервере) или в фоновом задании, потому что в тонком и веб-клиенте этот объект недоступен.
Средство одинаково для всех конфигураций на платформе 8.3: Бухгалтерии, УТ, ERP или самописной базы. Отличается только то, где хранить настройки подключения и откуда брать сам файл. Ниже — рабочий код, выгрузка без промежуточного файла на диске, обработка сбоев, выбор режима передачи и защита пароля.
Подготовка к записи файла на FTP: что проверить до кода
Большинство «не работает » на FTP — это не ошибка в модуле, а сеть или права. Пять минут проверки до программирования экономят день отладки.
📌 Что нужно знать о сервере:
- 🌐 адрес — имя хоста или IP, без префикса
ftp://; - 🔢 порт — обычно
21, у хостингов бывает свой; - 👤 логин и пароль пользователя с правом записи в целевой каталог;
- 📁 путь к каталогу, куда класть файлы, например
/upload/1c/; - 🔐 требуется ли шифрование (FTPS) или это вообще SFTP.
Откуда пойдёт соединение
Код FTPСоединение выполняется там, где работает серверный вызов. В клиент-серверной базе это компьютер с рабочим процессом сервера 1С, а не ПК пользователя. Значит, порт 21 и диапазон портов данных должны быть открыты именно с сервера 1С.
Проверить доступность проще всего штатным FTP-клиентом прямо с этой машины. Если с сервера 1С не удаётся зайти на FTP сторонней программой, 1С тоже не зайдёт.
Файловая база работает иначе: серверные процедуры выполняются на компьютере пользователя. Поэтому у одного сотрудника выгрузка проходит, а у другого — нет, если у него иначе настроен брандмауэр.
Безопасный режим и облако
Если код вызывается из расширения или внешней обработки в безопасном режиме, сетевые обращения ограничены профилем безопасности. В облачных сервисах 1С исходящие соединения на произвольные FTP тоже могут быть закрыты. Тогда нужен либо профиль с разрешением на нужный адрес и порт, либо другой способ передачи.
Создание соединения: параметры конструктора FTPСоединение
Конструктор принимает параметры по порядку. Обязателен только сервер, остальные можно пропускать запятыми.
| № | Параметр | Что передать |
|---|---|---|
| 1 | Сервер | "ftp.example.ru" или IP |
| 2 | Порт | 21 |
| 3 | Пользователь | логин |
| 4 | Пароль | пароль |
| 5 | Прокси | объект ИнтернетПрокси или Неопределено |
| 6 | ПассивноеСоединение | Истина — пассивный режим |
| 7 | Таймаут | секунды, 0 — без ограничения |
| 8 | ЗащищенноеСоединение | Новый ЗащищенноеСоединениеOpenSSL для FTPS |
Минимальное подключение выглядит так:
&НаСервере
Функция ПодключитьсяКFTP()
Соединение = Новый FTPСоединение(
"ftp.example.ru", // сервер
21, // порт
"user1c", // пользователь
"Pa$$w0rd", // пароль — в рабочем коде не хранить в модуле
Неопределено, // прокси
Истина, // пассивный режим
60); // таймаут, секунд
Возврат Соединение;
КонецФункции
Если в сети есть прокси, передайте Новый ИнтернетПрокси(Истина) — так берутся системные настройки прокси. В типовых конфигурациях на БСП прокси задают в общих настройках программы, и код может прочитать их через ПолучениеФайловИзИнтернета.ПолучитьПрокси("ftp").
Конструктор уже подключается к серверу. Неверный пароль или закрытый порт вызывают исключение прямо в строке Новый FTPСоединение, поэтому её тоже оборачивают в Попытка.
Как записать файл на FTP сервер: пошаговый алгоритм
Сам процесс записи укладывается в четыре действия.
- Создать соединение с нужными параметрами.
- Перейти в целевой каталог методом
УстановитьТекущийКаталог. - Вызвать
Записатьс полным локальным путём и именем файла на сервере. - Отпустить соединение: присвоить переменной
Неопределено.
Полный пример выгрузки файла
&НаСервере
Процедура ВыгрузитьФайлНаFTP(ЛокальныйПуть, ИмяНаСервере)
Соединение = Новый FTPСоединение("ftp.example.ru", 21,
"user1c", "Pa$$w0rd", , Истина, 60);
Соединение.УстановитьТекущийКаталог("/upload/1c/");
Соединение.Записать(ЛокальныйПуть, ИмяНаСервере);
Соединение = Неопределено;
КонецПроцедуры
Вызов: ВыгрузитьФайлНаFTP("D:\Обмен\prices.xml", "prices.xml"). Путь D:\Обмен\ должен существовать на сервере 1С, а не на компьютере, где открыта форма.
Если каталога на сервере ещё нет
УстановитьТекущийКаталог упадёт с ошибкой, если папки нет. Перед переходом проверьте её наличие через НайтиФайлы и при необходимости создайте СоздатьКаталог:
Найденные = Соединение.НайтиФайлы("/upload/", "1c");
Если Найденные.Количество() = 0 Тогда
Соединение.СоздатьКаталог("/upload/1c");
КонецЕсли;
Соединение.УстановитьТекущийКаталог("/upload/1c/");
СоздатьКаталог не строит всю цепочку сразу: для пути /a/b/c каталоги a и b уже должны быть.
Атомарная запись: сначала временное имя
Приём, который знают те, кто настраивал обмены с сайтами. Если принимающая сторона забирает файл по расписанию, она может схватить его недописанным. Поэтому файл сначала пишут под временным именем, а потом переименовывают:
Соединение.Записать(ЛокальныйПуть, "prices.xml.part");
Соединение.Удалить("prices.xml"); // если старая версия есть
Соединение.Переместить("prices.xml.part", "prices.xml");
Переименование на FTP выполняется мгновенно, и скрипт на сайте увидит только готовый файл. Вызов Удалить для несуществующего файла на части серверов даёт ошибку, поэтому сначала проверьте наличие через НайтиФайлы.
Проверка, что файл дошёл целиком
После записи сравните размер на сервере с локальным:
РазмерЛокальный = Новый Файл(ЛокальныйПуть).Размер();
Найденные = Соединение.НайтиФайлы("/upload/1c/", "prices.xml");
Если Найденные.Количество() = 0
Или Найденные[0].Размер() <> РазмерЛокальный Тогда
ВызватьИсключение "Файл на FTP не совпадает с исходным по размеру";
КонецЕсли;
Выгрузка на FTP без готового файла на диске
Часто выгружать нужно не лежащий на диске файл, а данные, которые 1С только что собрала: XML прайса, CSV остатков, присоединённый к документу скан. Метод Записать работает с путём к файлу, поэтому схема такая: данные → ДвоичныеДанные → временный файл → FTP → удалить временный файл.
Текст или XML, собранный в памяти
&НаСервере
Процедура ВыгрузитьТекстНаFTP(Текст, ИмяНаСервере)
ИмяВременного = ПолучитьИмяВременногоФайла("xml");
ДД = ПолучитьДвоичныеДанныеИзСтроки(Текст, КодировкаТекста.UTF8);
ДД.Записать(ИмяВременного);
Попытка
Соединение = Новый FTPСоединение("ftp.example.ru", 21,
"user1c", "Pa$$w0rd", , Истина, 60);
Соединение.УстановитьТекущийКаталог("/upload/1c/");
Соединение.Записать(ИмяВременного, ИмяНаСервере);
Исключение
УдалитьФайлы(ИмяВременного);
ВызватьИсключение;
КонецПопытки;
УдалитьФайлы(ИмяВременного);
КонецПроцедуры
Поток в памяти и двоичные данные
Если данные формируются порциями, удобно писать их в ПотокВПамяти, а в конце вызвать ЗакрытьИПолучитьДвоичныеДанные(). Дальше — то же самое: ДД.Записать(ИмяВременного) и выгрузка.
Для файла из хранилища значений или присоединённых файлов БСП сначала получите ДвоичныеДанные, а затем сохраните их во временный файл.
| Источник данных | Как получить ДвоичныеДанные |
|---|---|
| Строка (XML, CSV, JSON) | ПолучитьДвоичныеДанныеИзСтроки(Текст, КодировкаТекста.UTF8) |
| Порции данных | ПотокВПамяти → ЗакрытьИПолучитьДвоичныеДанные() |
| Реквизит типа ХранилищеЗначения | Реквизит.Получить() |
| Временное хранилище | ПолучитьИзВременногоХранилища(Адрес) |
Принимающие скрипты на PHP или Python иногда спотыкаются о BOM — служебную метку в начале UTF-8 файла. Чтобы её не было, передайте третьим параметром ПолучитьДвоичныеДанныеИзСтроки значение ИспользованиеByteOrderMark.НеИспользовать.
Обработка ошибок и сетевых сбоев при работе с FTP
FTP — сетевая операция, сбой здесь штатная ситуация. Код без Попытка...Исключение в регламентном задании просто упадёт, и никто об этом не узнает.
Что ловить и куда писать
Текст ошибки берите из ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()) и пишите в журнал регистрации. Так администратор увидит его в Администрирование → Обслуживание → Журнал регистрации, отобрав по событию.
Исключение
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Обмен.ВыгрузкаFTP",
УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
КонецПопытки;
Повторные попытки
Кратковременный обрыв лечится повтором. Три попытки с паузой между ними закрывают большую часть случайных сбоев, а на постоянную ошибку (неверный пароль) не тратят много времени.
&НаСервере
Функция ЗаписатьСПовторами(ЛокальныйПуть, ИмяНаСервере, Попыток = 3)
Для Номер = 1 По Попыток Цикл
Попытка
Соединение = Новый FTPСоединение("ftp.example.ru", 21,
"user1c", "Pa$$w0rd", , Истина, 60);
Соединение.УстановитьТекущийКаталог("/upload/1c/");
Соединение.Записать(ЛокальныйПуть, ИмяНаСервере);
Возврат Истина;
Исключение
ЗаписьЖурналаРегистрации("Обмен.ВыгрузкаFTP",
УровеньЖурналаРегистрации.Предупреждение, , ,
СтрШаблон("Попытка %1: %2", Номер,
КраткоеПредставлениеОшибки(ИнформацияОбОшибке())));
Если Номер < Попыток Тогда
ФоновыеЗадания.ТекущееФоновоеЗадание().ОжидатьЗавершенияВыполнения(10);
КонецЕсли;
КонецПопытки;
КонецЦикла;
Возврат Ложь;
КонецФункции
Пауза через ОжидатьЗавершенияВыполнения работает внутри фонового или регламентного задания. При вызове из формы текущего фонового задания нет, поэтому там паузу убирают или выносят выгрузку в фон.
Расшифровка типовых ошибок
Активный или пассивный режим передачи
FTP использует два канала: управляющий (порт 21) и канал данных. Режим определяет, кто открывает второй канал.
| Режим | Кто инициирует канал данных | Когда выбирать |
|---|---|---|
Пассивный (Истина) | клиент, то есть сервер 1С, на порт, названный FTP-сервером | почти всегда: сервер 1С за NAT или брандмауэром |
Активный (Ложь) | FTP-сервер подключается обратно к серверу 1С | только если FTP-сервер требует и входящие подключения к серверу 1С открыты |
Типичная картина при неверном режиме: логин проходит, каталог меняется, а на Записать код висит до таймаута. Управляющий канал работает, канал данных — нет.
Задавайте шестой параметр явно, даже если устраивает значение по умолчанию. Так через год другой программист сразу увидит, в каком режиме работает обмен.
Защита пароля, FTPS и уборка временных файлов
Где хранить логин и пароль
Пароль в тексте модуля увидит любой, у кого есть доступ к Конфигуратору или к выгрузке конфигурации. Варианты лучше:
- 🔒 в конфигурациях на БСП — безопасное хранилище:
ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилищеиОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища; - 🗂 в своей базе — отдельный регистр сведений с правами только у администратора и чтением в привилегированном режиме;
- ⚙️ параметры регламентного задания — компромисс, если задание настраивает только администратор.
Пользователю FTP дайте права только на запись в один каталог. Если учётку украдут, ущерб ограничится этой папкой.
Шифрование: FTPS
Обычный FTP передаёт пароль и данные открытым текстом. Если сервер поддерживает FTPS (FTP поверх TLS), передайте восьмым параметром Новый ЗащищенноеСоединениеOpenSSL(). Без аргументов проверка сертификата сервера не выполняется; для проверки передают сертификаты удостоверяющих центров, например Новый СертификатыУдостоверяющихЦентровОС.
Если нужен SFTP
SFTP — это другой протокол поверх SSH, FTPСоединение с ним не работает. Рабочий путь — внешняя утилита командной строки, например WinSCP, которую вызывают из 1С через КомандаСистемы или ЗапуститьПриложение со скриптом:
winscp.com /command "open sftp://user1c@sftp.example.ru/ -hostkey=""ssh-ed25519 255 ...""" "put D:\Обмен\prices.xml /upload/1c/" "exit"
Утилиту ставят на сервер 1С, ключ хоста фиксируют параметром -hostkey, а результат проверяют по коду возврата.
Временные файлы
ПолучитьИмяВременногоФайла только придумывает имя в каталоге временных файлов, сам файл никто не удалит. При ежечасной выгрузке прайса на 20 МБ за месяц накопится больше 14 ГБ мусора. Поэтому:
- Удаляйте временный файл
УдалитьФайлы(ИмяВременного)сразу после записи. - Повторяйте удаление в ветке
Исключение— иначе при сбое файл останется. - Не кладите временные файлы в общий каталог обмена, где их может забрать другой процесс.
Частые вопросы
Можно ли записать на FTP прямо из тонкого клиента?
FTPСоединение в тонком и веб-клиенте недоступен. Вызывайте серверную процедуру с директивой &НаСервере или запускайте выгрузку фоновым заданием.Как выгружать файл на FTP по расписанию?
Перезапишет ли метод Записать существующий файл?
Удалить перед записью.Почему не работают имена файлов с кириллицей?
Как выгрузить сразу несколько файлов?
Записать для каждого файла. Создавать новое соединение на каждый файл не нужно — это медленнее и нагружает FTP-сервер.