При интеграции 1С с внешними системами (например, RetailCRM, Bitrix24 или почтовыми сервисами) часто возникает задача скачивания файлов. В отличие от получения текстовых данных в формате JSON или XML, скачивание файлов требует работы с двоичными данными. Часто API отдает файл в виде потока байт, а метаданные (например, имя файла) передаются в HTTP-заголовках. В этой статье мы разберем, как правильно реализовать такой механизм в 1С, избегая типичных ошибок с кодировками и временными файлами.
Разберем ситуацию, когда в наследственном коде или старых обработках используется метод HTTPСоединение.Получить() с записью результата во временный файл. Часто разработчики пытаются прочитать этот файл как текст, что приводит к порче бинарных данных (картинок, документов PDF или DOCX). Рассмотрим типичную ошибку:
ИмяФайлаОтвета = ПолучитьИмяВременногоФайла;
HTTP_Соединение.Получить(АдресСкрипта, ИмяФайлаОтвета);
ТекстОтвета = Новый ТекстовыйДокумент;
ТекстОтвета.Прочитать(ИмяФайлаОтвета, "utf-8"); // ОШИБКА: Бинарный файл нельзя читать как текст!
Возврат ТекстОтвета.ПолучитьТекст();
Проанализируем причину проблемы: файл, приходящий от API, является набором байт. Попытка прочитать его через ТекстовыйДокумент с кодировкой UTF-8 приведет к тому, что спецсимволы бинарного потока будут интерпретированы неверно, и файл будет "битым". Для корректной работы нам необходимо использовать объект HTTPЗапрос и получать данные напрямую в память или сразу на диск.
Начиная с версии платформы 8.3.3, рекомендуется использовать объекты HTTPЗапрос и HTTPОтвет. Это позволяет гибко управлять заголовками и получать доступ к телу ответа в виде объекта ДвоичныеДанные. Рассмотрим правильную реализацию метода GET для скачивания файла:
Функция СкачатьФайлССервера(АдресСервера, АдресСкрипта) Экспорт
// Настройка безопасного соединения
SSL = Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено);
HTTPСоединение = Новый HTTPСоединение(АдресСервера, , , , , , SSL);
// Формируем запрос
Запрос = Новый HTTPЗапрос(АдресСкрипта);
// Выполняем запрос
Ответ = HTTPСоединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Получаем данные напрямую в двоичном виде
ДанныеФайла = Ответ.ПолучитьТелоКакДвоичныеДанные();
Возврат ДанныеФайла;
Иначе
Сообщить("Ошибка при скачивании: " + Ответ.КодСостояния);
Возврат Неопределено;
КонецЕсли;
КонецФункции
Часто API не передает имя файла в URL, а возвращает его в специальном заголовке Content-Disposition. Выясним, как извлечь это имя. Значение заголовка обычно выглядит так: attachment; filename=test.docx. Нам нужно найти подстроку после "filename=".
Обратите внимание на распространенную ошибку при использовании функции СтрНайти(). Помните, что первым параметром идет строка, в которой ищем, а вторым — что ищем. Разберем правильный пример получения имени:
Заголовок = Ответ.Заголовки.Получить("Content-Disposition");
Если Заголовок <> Неопределено Тогда
// Ищем позицию начала имени файла
ПозицияИмени = СтрНайти(Заголовок, "filename=");
Если ПозицияИмени > 0 Тогда
ИмяФайла = Сред(Заголовок, ПозицияИмени + 9); // 9 - это длина строки "filename="
// Убираем лишние кавычки, если они есть
ИмяФайла = СтрЗаменить(ИмяФайла, """", "");
КонецЕсли;
КонецЕсли;
Если файл весит несколько сотен мегабайт, метод ПолучитьТелоКакДвоичныеДанные() может вызвать переполнение оперативной памяти сервера 1С, так как он загружает весь файл целиком. Проанализируем более эффективный способ — запись потока сразу на диск. Для этого в метод Получить() нужно передать путь к файлу вторым параметром.
ПутьДляСохранения = ПолучитьИмяВременногоФайла("docx");
// 1С запишет поток данных из HTTP-ответа сразу в файл на диске
Ответ = HTTPСоединение.Получить(Запрос, ПутьДляСохранения);
Если Ответ.КодСостояния = 200 Тогда
// Файл уже на диске, можем создать объект ДвоичныеДанные из него, если нужно
Результат = Новый ДвоичныеДанные(ПутьДляСохранения);
КонецЕсли;
Современные API часто передают кириллические имена файлов в формате filename*=utf-8''%D0%A2%D0%B5.... Если вы просто извлечете строку, вы получите "кракозябры". Для корректного отображения русского названия необходимо выполнить URL-декодирование. В 1С это делается с помощью функции КодироватьСтроку с обратным параметром.
// Пример декодирования части имени файла
ИсходноеИмя = "%D0%A2%D0%B5%D1%81%D1%82.docx";
ИмяДекодированное = КодироватьСтроку(ИсходноеИмя, СпособКодированияСтроки.КодировкаURL, "UTF-8");
// В результате получим: Тест.docx
Рассмотрим важный аспект работы с RetailCRM и аналогичными системами. Хотя API-ключ можно передавать в параметрах URL (?apiKey=...), это считается менее безопасным, так как ключи попадают в логи серверов. Рекомендуем передавать авторизационные данные в заголовках запроса:
Запрос = Новый HTTPЗапрос(АдресСкрипта);
Запрос.Заголовки.Вставить("X-API-KEY", "ВАШ_КЛЮЧ_API");
Ответ = HTTPСоединение.Получить(Запрос);
После того как мы успешно получили ДвоичныеДанные на сервере, их необходимо передать на клиент для открытия пользователем. Разберем этот финальный шаг:
Адрес = ПоместитьВоВременноеХранилище(Данные, ЭтаФорма.УникальныйИдентификатор);.Адрес на клиент.ПолучитьФайл(Адрес, ИмяФайла, Истина);.Такой подход гарантирует, что файл будет корректно загружен, сохранен с правильным именем и расширением, а ресурсы сервера будут использованы максимально эффективно.