При работе с внешними API и сервисами часто возникает задача загрузки файлов значительного объема (от нескольких сотен мегабайт до нескольких гигабайт). Использование стандартного метода ПолучитьТелоКакДвоичныеДанные() в таких случаях может привести к критической ошибке нехватки оперативной памяти, так как платформа пытается загрузить весь файл в ОЗУ перед сохранением. Рассмотрим, как правильно использовать механизмы потоков в 1С для эффективного решения этой задачи.
Проанализируем стандартную ситуацию: разработчик выполняет HTTPСоединение.Получить() и сохраняет результат в переменную. Если файл весит 2 ГБ, 1С попытается выделить соответствующий объем памяти. Если таких файлов много или сервер имеет ограниченные ресурсы, процесс аварийно завершится. Выясним причину: объект ДвоичныеДанные является контейнером, который по умолчанию стремится находиться в памяти. Для оптимизации нам необходимо передавать данные из сетевого сокета напрямую на жесткий диск, минуя стадию полного кеширования в ОЗУ.
Самый простой и зачастую самый эффективный способ — использование второго параметра метода Получить() объекта HTTPСоединение. Платформа 1С:Предприятие на уровне ядра поддерживает потоковую запись ответа в файл, если указано имя целевого файла.
Рассмотрим пример кода:
// Определяем путь, куда будет сохранен файл
ПутьДляСохранения = "C:\Downloads\bigfile.zip";
// Создаем соединение
Соединение = Новый HTTPСоединение("example.com", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/api/get_large_file");
// Выполняем запрос, указывая путь к файлу вторым параметром
Ответ = Соединение.Получить(Запрос, ПутьДляСохранения);
// В данном случае Ответ.ПолучитьТелоКакДвоичныеДанные() вернет пустое значение,
// так как данные уже записаны на диск.
Если Ответ.КодСостояния = 200 Тогда
Сообщить("Файл успешно загружен напрямую на диск.");
КонецЕсли;
Этот метод является предпочтительным, так как он минимизирует количество строк кода и использует внутренние оптимизированные алгоритмы платформы.
Иногда нам требуется более гибкое управление процессом: например, сначала проанализировать заголовки ответа (проверить тип контента или статус), а только потом принять решение о сохранении. В этом случае на помощь приходит метод ПолучитьТелоКакПоток().
Разберем по шагам алгоритм работы с потоками:
HTTPОтвет.Посмотрим на пример реализации:
Соединение = Новый HTTPСоединение("example.com");
Запрос = Новый HTTPЗапрос("/data/export");
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Получаем входящий поток данных от сервера
ПотокОтвета = Ответ.ПолучитьТелоКакПоток();
// Создаем поток для записи в файл на диске
ПутьКФайлу = "D:\Data\export.dat";
ПотокФайла = Новый ПотокВФайле(ПутьКФайлу, РежимОткрытияФайла.Создать);
// Используем встроенный метод копирования (доступен с 8.3.10)
// Данный метод автоматически управляет буфером и не переполняет память
ПотокОтвета.КопироватьВ(ПотокФайла);
// Обязательно закрываем потоки для освобождения ресурсов
ПотокФайла.Закрыть();
ПотокОтвета.Закрыть();
Сообщить("Потоковая загрузка завершена.");
КонецЕсли;
Если версия платформы ниже 8.3.10 или требуется обрабатывать данные в процессе чтения (например, для отображения прогресс-бара), мы можем реализовать чтение порциями вручную с помощью объектов ЧтениеДанных и ЗаписьДанных.
Рассмотрим подробнее, как это работает:
ПотокОтвета = Ответ.ПолучитьТелоКакПоток();
Чтение = Новый ЧтениеДанных(ПотокОтвета);
ПотокФайла = Новый ПотокВФайле("C:\temp\result.bin", РежимОткрытияФайла.Создать);
Запись = Новый ЗаписьДанных(ПотокФайла);
// Указываем размер буфера, например, 1 МБ (1024 * 1024 байт)
РазмерБуфера = 1048576;
Пока Истина Цикл
// Читаем порцию данных
ДанныеПорция = Чтение.Прочитать(РазмерБуфера);
// Если прочитано 0 байт - поток закончился
Если ДанныеПорция.Размер() = 0 Тогда
Прервать;
КонецЕсли;
// Записываем порцию в файл
Запись.Записать(ДанныеПорция);
КонецЦикла;
Запись.Закрыть();
Чтение.Закрыть();
Этот метод позволяет полностью контролировать нагрузку на систему и дает возможность вставить обработчик прерывания пользователем или обновление индикатора прогресса внутри цикла.
При работе с потоками следует учитывать несколько критически важных моментов:
HTTPСоединение установлены адекватные значения таймаутов (параметры Таймаут), иначе соединение разорвется до завершения чтения потока.Закрыть() для объектов Поток, ЧтениеДанных и ЗаписьДанных. В противном случае файл может быть заблокирован операционной системой, а сетевое соединение останется "висеть".Попытка...Исключение. При обрыве связи поток может стать недоступным, и программа вызовет исключение.КопироватьВ или цикл чтения по буферу — единственный надежный способ загрузки, так как 1С не сможет заранее выделить память под неизвестный объем данных.Таким образом, использование HTTPОтвет.ПолучитьТелоКакПоток() в сочетании с ПотокВФайле является профессиональным стандартом при разработке интеграций в 1С:Предприятие 8.3, обеспечивая стабильность системы при работе с любыми объемами данных.