Как скачать и сохранить файл из потока данных API внешней системы в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними системами (например, RetailCRM, Bitrix24 или почтовыми сервисами) часто возникает задача скачивания файлов. В отличие от получения текстовых данных в формате JSON или XML, скачивание файлов требует работы с двоичными данными. Часто API отдает файл в виде потока байт, а метаданные (например, имя файла) передаются в HTTP-заголовках. В этой статье мы разберем, как правильно реализовать такой механизм в 1С, избегая типичных ошибок с кодировками и временными файлами.

Проблема использования устаревшего синтаксиса

Разберем ситуацию, когда в наследственном коде или старых обработках используется метод HTTPСоединение.Получить() с записью результата во временный файл. Часто разработчики пытаются прочитать этот файл как текст, что приводит к порче бинарных данных (картинок, документов PDF или DOCX). Рассмотрим типичную ошибку:


ИмяФайлаОтвета = ПолучитьИмяВременногоФайла;
HTTP_Соединение.Получить(АдресСкрипта, ИмяФайлаОтвета);
ТекстОтвета = Новый ТекстовыйДокумент;
ТекстОтвета.Прочитать(ИмяФайлаОтвета, "utf-8"); // ОШИБКА: Бинарный файл нельзя читать как текст!
Возврат ТекстОтвета.ПолучитьТекст();

Проанализируем причину проблемы: файл, приходящий от API, является набором байт. Попытка прочитать его через ТекстовыйДокумент с кодировкой UTF-8 приведет к тому, что спецсимволы бинарного потока будут интерпретированы неверно, и файл будет "битым". Для корректной работы нам необходимо использовать объект HTTPЗапрос и получать данные напрямую в память или сразу на диск.

Современный подход: работа с HTTPЗапрос и HTTPОтвет

Начиная с версии платформы 8.3.3, рекомендуется использовать объекты HTTPЗапрос и HTTPОтвет. Это позволяет гибко управлять заголовками и получать доступ к телу ответа в виде объекта ДвоичныеДанные. Рассмотрим правильную реализацию метода GET для скачивания файла:


Функция СкачатьФайлССервера(АдресСервера, АдресСкрипта) Экспорт
    
    // Настройка безопасного соединения
    SSL = Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено);
    HTTPСоединение = Новый HTTPСоединение(АдресСервера, , , , , , SSL);
    
    // Формируем запрос
    Запрос = Новый HTTPЗапрос(АдресСкрипта);
    
    // Выполняем запрос
    Ответ = HTTPСоединение.Получить(Запрос);
    
    Если Ответ.КодСостояния = 200 Тогда
        // Получаем данные напрямую в двоичном виде
        ДанныеФайла = Ответ.ПолучитьТелоКакДвоичныеДанные();
        Возврат ДанныеФайла;
    Иначе
        Сообщить("Ошибка при скачивании: " + Ответ.КодСостояния);
        Возврат Неопределено;
    КонецЕсли;
    
КонецФункции

Как получить имя файла из заголовка Content-Disposition

Часто API не передает имя файла в URL, а возвращает его в специальном заголовке Content-Disposition. Выясним, как извлечь это имя. Значение заголовка обычно выглядит так: attachment; filename=test.docx. Нам нужно найти подстроку после "filename=".

Обратите внимание на распространенную ошибку при использовании функции СтрНайти(). Помните, что первым параметром идет строка, в которой ищем, а вторым — что ищем. Разберем правильный пример получения имени:


Заголовок = Ответ.Заголовки.Получить("Content-Disposition");
Если Заголовок <> Неопределено Тогда
    // Ищем позицию начала имени файла
    ПозицияИмени = СтрНайти(Заголовок, "filename=");
    Если ПозицияИмени > 0 Тогда
        ИмяФайла = Сред(Заголовок, ПозицияИмени + 9); // 9 - это длина строки "filename="
        // Убираем лишние кавычки, если они есть
        ИмяФайла = СтрЗаменить(ИмяФайла, """", "");
    КонецЕсли;
КонецЕсли;

Оптимизация: сохранение больших файлов (потоковый метод)

Если файл весит несколько сотен мегабайт, метод ПолучитьТелоКакДвоичныеДанные() может вызвать переполнение оперативной памяти сервера 1С, так как он загружает весь файл целиком. Проанализируем более эффективный способ — запись потока сразу на диск. Для этого в метод Получить() нужно передать путь к файлу вторым параметром.


ПутьДляСохранения = ПолучитьИмяВременногоФайла("docx");
// 1С запишет поток данных из HTTP-ответа сразу в файл на диске
Ответ = HTTPСоединение.Получить(Запрос, ПутьДляСохранения);

Если Ответ.КодСостояния = 200 Тогда
    // Файл уже на диске, можем создать объект ДвоичныеДанные из него, если нужно
    Результат = Новый ДвоичныеДанные(ПутьДляСохранения);
КонецЕсли;

Декодирование имени файла (RFC 5987)

Современные 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Соединение.Получить(Запрос);

Помещение файла во временное хранилище

После того как мы успешно получили ДвоичныеДанные на сервере, их необходимо передать на клиент для открытия пользователем. Разберем этот финальный шаг:

  1. Получаем данные на сервере через HTTP-запрос.
  2. Помещаем их во временное хранилище: Адрес = ПоместитьВоВременноеХранилище(Данные, ЭтаФорма.УникальныйИдентификатор);.
  3. Передаем Адрес на клиент.
  4. На клиенте вызываем ПолучитьФайл(Адрес, ИмяФайла, Истина);.

Такой подход гарантирует, что файл будет корректно загружен, сохранен с правильным именем и расширением, а ресурсы сервера будут использованы максимально эффективно.

← На главную