При активной работе с внешними FTP-серверами разработчики на платформе 1С:Предприятие часто сталкиваются со странным и нелогичным поведением встроенного FTP-клиента. Наиболее распространенные проблемы возникают, когда в именах файлов или каталогов содержатся специальные символы (особенно символ коммерческого "at" — @) или концевые пробелы. Платформа начинает некорректно возвращать имена файлов, обрезать их, а при попытке скачать, переименовать или удалить объект выбрасывает ошибки уровня "Access denied to remote resource" или "Путь не найден".
В этой статье мы подробно разберем внутренние механизмы платформы, которые приводят к возникновению этих багов, проанализируем причины их появления и рассмотрим несколько эффективных практических способов обхода ограничений встроенного FTP-клиента 1С.
Разберем ситуацию на примере. Допустим, на FTP-сервере создан каталог с именем e-mail@mail.ru. При вызове метода НайтиФайлы() платформа 1С возвращает объект типа FTPФайл. Однако при обращении к свойствам этого объекта мы обнаруживаем следующие странности:
ПолноеИмя возвращает абсолютно корректный путь: /Папка/e-mail@mail.ru.Имя внезапно возвращает только часть строки до собаки: e-mail.Расширение возвращает пустую строку, так как точка в доменной части имени была отрезана вместе со всей правой частью строки.Причина этого кроется в том, что внутренний парсер платформы 1С для работы с сетевыми путями жестко опирается на стандарт RFC 3986 (формат записи URL). В соответствии с этим стандартом символ @ является строго зарезервированным служебным разделителем. Он отделяет блок авторизации (логин и пароль) от адреса хоста в структуре строки подключения: ftp://логин:пароль@хост/путь.
Когда платформа пытается заполнить свойства Имя и ИмяБезРасширения объекта FTPФайл, она пропускает путь через внутренний парсер URL. Обнаружив в середине пути символ @, парсер ошибочно решает, что перед ним находится разделитель авторизационных данных. В итоге вся правая часть имени файла отсекается как служебная, а программист получает урезанную строку.
Ситуация усугубляется при попытке выполнить скачивание или поиск файлов через методы Получить() или НайтиФайлы(). Передавая в параметры путь, содержащий сырой символ @, мы заставляем 1С заново конструировать URL-запрос. Парсер сбивается и считает часть пути до собаки именем пользователя, а часть после собаки — адресом FTP-сервера. В результате 1С пытается выполнить повторную авторизацию с искаженным логином и паролем, что мгновенно приводит к ошибке: Ошибка аутентификации при доступе к ресурсу: ftp://... Access denied to remote resource.
Многие разработчики пытаются решить проблему "в лоб" с помощью стандартного URL-кодирования (Percent-encoding), заменяя символ @ на его шестнадцатеричный эквивалент %40. На этапе поиска файлов это действительно частично помогает, так как парсер URL успешно распознает экранированный символ.
Однако при попытке удалить файл с помощью метода Удалить() или записать новый файл через метод Записать() нас ждет неприятный сюрприз: платформа возвращает ошибку Quote command returned error или Путь не найден. Это происходит из-за архитектурных особенностей платформы:
НайтиФайлы) работают через высокоуровневые механизмы запросов, где URL-кодирование корректно обрабатывается.Удалить и Записать) транслируют команды напрямую в низкоуровневый протокол FTP (команды DELE и STOR соответственно). Платформа отправляет имя файла на сервер "как есть", без обратного декодирования. В итоге сервер получает сырую команду на удаление файла, в имени которого буквально содержится строка %40. Естественно, сервер не находит файла с таким физическим именем и возвращает ошибку.Еще одна неприятная особенность встроенного клиента 1С — это автоматическое обрезание пробелов в начале и в конце имен файлов и каталогов. Если на FTP-сервере создана папка, имя которой заканчивается пробелом (например, "Папка "), то при получении списка файлов платформа применит к имени аналог функции СокрЛП().
Происходит это на этапе разбора текстового ответа от FTP-сервера на команду LIST. Большинство серверов отдают список в Unix-подобном формате. Платформа 1С парсит этот текстовый поток построчно, разделяет колонки и автоматически "очищает" строковые значения от лишних, по ее мнению, пробелов. В результате в коллекцию файлов попадает объект с именем "Папка" (без пробела). Любая последующая попытка обратиться к этому объекту завершится ошибкой "Путь не найден", так как на самом сервере папка по-прежнему имеет пробел на конце.
Теперь, когда мы детально разобрали причины возникновения этих ошибок, рассмотрим практические сценарии их обхода.
Если ваша задача ограничивается чтением структуры каталогов, получением списка файлов и анализом их реальных имен (без необходимости удаления или записи на стороне 1С), самым простым решением будет отказаться от использования проблемных свойств Имя и ИмяБезРасширения.
Как мы выяснили, свойство ПолноеИмя объекта FTPФайл заполняется напрямую из сырого ответа сервера и содержит абсолютно корректный путь вместе со всеми символами @ и расширениями. Напишем собственную функцию-обертку, которая будет безопасно извлекать реальное имя файла из полного пути.
Рассмотрим пример кода:
// Функция извлекает реальное имя файла/каталога из полного пути
Функция ПолучитьРеальноеИмяФайла(ОбъектFTPФайл) Экспорт
ПолныйПуть = ОбъектFTPФайл.ПолноеИмя;
// В путях FTP всегда используются прямые слеши "/"
ПозицияСлеша = СтрНайти(ПолныйПуть, "/", НаправлениеПоиска.СКонца);
Если ПозицияСлеша > 0 Тогда
Возврат Сред(ПолныйПуть, ПозицияСлеша + 1);
Иначе
Возврат ПолныйПуть;
КонецЕсли;
КонецФункции
// Пример использования функции в коде
Процедура ПолучитьСписокФайлов()
Соединение = Новый FTPСоединение("ftp.server.ru", 21, "user", "password");
НайденныеФайлы = Соединение.НайтиФайлы("/КаталогСДанными/", "*", Ложь);
Для Каждого Файл Из НайденныеФайлы Цикл
// Вместо Файл.Имя используем нашу безопасную функцию
РеальноеИмя = ПолучитьРеальноеИмяФайла(Файл);
Сообщить("Обработка файла: " + РеальноеИмя);
КонецЦикла;
КонецПроцедуры
Этот способ полностью решает проблему чтения имен со спецсимволами, однако он бессилен, если файлы с символом @ или концевыми пробелами требуется удалять, перемещать или записывать на сервер программными методами 1С.
Для промышленных решений, где критически важна надежность и стабильность работы с любыми именами файлов на FTP, лучшим выходом является отказ от встроенного FTP-клиента 1С в пользу внешних специализированных консольных утилит. Отличным выбором является утилита cURL, которая предустановлена во всех современных операционных системах Windows и Linux.
Консольный клиент cURL написан на C++, строго следует сетевым спецификациям и не имеет детских болезней с парсингом спецсимволов или обрезанием пробелов. Мы можем запускать команды cURL из кода 1С с помощью метода ЗапуститьПриложение().
Рассмотрим пример скачивания файла со сложным именем, содержащим символ @:
Процедура СкачатьФайлЧерезCurl(ИмяФайлаНаFTP, ЛокальныйПутьДляСохранения)
СерверFTP = "ftp.server.ru";
ЛогинFTP = "user";
ПарольFTP = "password";
// Формируем параметры запуска cURL.
// Двойные кавычки вокруг путей защищают от пробелов в именах файлов.
ШаблонКоманды = "curl -u ""%1:%2"" ""ftp://%3/%4"" -o ""%5""";
СтрокаКоманды = СтрШаблон(ШаблонКоманды,
ЛогинFTP,
ПарольFTP,
СерверFTP,
ИмяФайлаНаFTP,
ЛокальныйПутьДляСохранения);
// Запускаем приложение в скрытом режиме и ожидаем завершения работы
КодВозврата = 0;
ЗапуститьПриложение(СтрокаКоманды, , Истина, КодВозврата);
Если КодВозврата <> 0 Тогда
ВызватьИсключение "Ошибка при скачивании файла через cURL. Код возврата: " + КодВозврата;
КонецЕсли;
Сообщить("Файл успешно скачан!");
КонецПроцедуры
Для удаления проблемного файла с FTP-сервера с помощью cURL можно использовать отправку сырых FTP-команд через флаг -X (или --request):
Процедура УдалитьФайлЧерезCurl(ПутьКФайлуНаFTP)
СерверFTP = "ftp.server.ru";
ЛогинFTP = "user";
ПарольFTP = "password";
// Для удаления отправляем команду DELE через cURL
ШаблонКоманды = "curl -u ""%1:%2"" ""ftp://%3/"" -Q ""DELE %4""";
СтрокаКоманды = СтрШаблон(ШаблонКоманды,
ЛогинFTP,
ПарольFTP,
СерверFTP,
ПутьКФайлуНаFTP);
КодВозврата = 0;
ЗапуститьПриложение(СтрокаКоманды, , Истина, КодВозврата);
Если КодВозврата <> 0 Тогда
ВызватьИсключение "Не удалось удалить файл. Код возврата: " + КодВозврата;
КонецЕсли;
Сообщить("Файл успешно удален с сервера!");
КонецПроцедуры
Если ваше решение работает исключительно под управлением операционной системы Windows, прекрасной альтернативой встроенному FTP-клиенту станет использование бесплатной библиотеки WinSCP. Она поставляется в виде сборки .NET и COM-объекта (файл WinSCPnet.dll), который можно легко зарегистрировать в системе и использовать в коде 1С.
Преимущество WinSCP заключается в том, что она обеспечивает полноценное объектно-ориентированное управление сессиями FTP/SFTP/FTPS, работает на порядок быстрее штатных механизмов 1С и абсолютно корректно обрабатывает любые кодировки, спецсимволы и пробелы.
Проанализируем пример подключения и скачивания файла через COM-объект WinSCP:
Процедура СкачатьЧерезWinSCP()
// Инициализируем COM-объект WinSCP
Попытка
СессияWinSCP = Новый COMОбъект("WinSCP.Session");
ОпцииСессии = Новый COMОбъект("WinSCP.SessionOptions");
Исключение
Сообщить("Компонент WinSCP не зарегистрирован в системе!");
Возврат;
КонецПопытки;
// Настраиваем параметры подключения
ОпцииСессии.Protocol = 1; // Указываем протокол FTP (0 - SFTP, 1 - FTP)
ОпцииСессии.HostName = "ftp.server.ru";
ОпцииСессии.UserName = "user";
ОпцииСессии.Password = "password";
Попытка
// Открываем сессию
СессияWinSCP.Open(ОпцииСессии);
// Настраиваем параметры передачи файлов
ОпцииПередачи = Новый COMОбъект("WinSCP.TransferOptions");
ОпцииПередачи.TransferMode = 0; // Двоичный режим передачи (Binary)
// Скачиваем файл с символом @ напрямую без искажения путей
ПутьНаFTP = "/Папка/e-mail@mail.ru";
ЛокальныйПуть = "C:\Temp\email.ru";
РезультатПередачи = СессияWinSCP.GetFiles(ПутьНаFTP, ЛокальныйПуть, Ложь, ОпцииПередачи);
// Проверяем, возникли ли ошибки во время передачи
РезультатПередачи.Check();
Сообщить("Файл успешно загружен через WinSCP!");
Исключение
Сообщить("Произошла ошибка при работе с WinSCP: " + ОписаниеОшибки());
ОчиститьВторостепенныеОбъекты:
Если СессияWinSCP.Opened Тогда
СессияWinSCP.Close();
КонецЕсли;
КонецПопытки;
КонецПроцедуры
Если вы имеете возможность влиять на бизнес-процессы смежных систем или контрагентов, которые выгружают данные на ваш FTP-сервер, самым разумным решением будет внедрение жесткого административного регламента наименования файлов.
Использование спецсимволов (таких как @, #, &, %, ?), символов кириллицы, а также пробелов (особенно в начале и конце имен) признано плохой практикой во всей ИТ-индустрии. Это связано с тем, что различные операционные системы и сетевые протоколы интерпретируют эти символы по-разному, что неизбежно приводит к сбоям при автоматизированном обмене.
Рекомендуемые правила для регламента обмена файлами через FTP:
- и нижнего подчеркивания _."Выгрузка данных.xml" использовать "vygruzka_dannyh.xml").@) в именах папок (например, почтовых ящиков контрагентов) следует заменять на дефис или полностью исключать из названия каталогов.Соблюдение этих простых правил позволит полностью исключить любые ошибки обмена данными еще на этапе проектирования интеграционных механизмов.