Как организовать потоковую загрузку больших файлов на диск в 1С:Предприятие?

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

При работе с внешними API и сервисами часто возникает задача загрузки файлов значительного объема (от нескольких сотен мегабайт до нескольких гигабайт). Использование стандартного метода ПолучитьТелоКакДвоичныеДанные() в таких случаях может привести к критической ошибке нехватки оперативной памяти, так как платформа пытается загрузить весь файл в ОЗУ перед сохранением. Рассмотрим, как правильно использовать механизмы потоков в 1С для эффективного решения этой задачи.

Проблема использования оперативной памяти при HTTP-запросах

Проанализируем стандартную ситуацию: разработчик выполняет HTTPСоединение.Получить() и сохраняет результат в переменную. Если файл весит 2 ГБ, 1С попытается выделить соответствующий объем памяти. Если таких файлов много или сервер имеет ограниченные ресурсы, процесс аварийно завершится. Выясним причину: объект ДвоичныеДанные является контейнером, который по умолчанию стремится находиться в памяти. Для оптимизации нам необходимо передавать данные из сетевого сокета напрямую на жесткий диск, минуя стадию полного кеширования в ОЗУ.

Способ 1. Использование штатного параметра метода Получить()

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

Рассмотрим пример кода:


// Определяем путь, куда будет сохранен файл
ПутьДляСохранения = "C:\Downloads\bigfile.zip";

// Создаем соединение
Соединение = Новый HTTPСоединение("example.com", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/api/get_large_file");

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

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

Этот метод является предпочтительным, так как он минимизирует количество строк кода и использует внутренние оптимизированные алгоритмы платформы.

Способ 2. Использование механизмов Поток и КопироватьВ (для версий 8.3.10+)

Иногда нам требуется более гибкое управление процессом: например, сначала проанализировать заголовки ответа (проверить тип контента или статус), а только потом принять решение о сохранении. В этом случае на помощь приходит метод ПолучитьТелоКакПоток().

Разберем по шагам алгоритм работы с потоками:

  1. Выполняем запрос и получаем объект HTTPОтвет.
  2. Получаем поток из тела ответа.
  3. Создаем целевой поток (в файле) на диске.
  4. Выполняем копирование данных из одного потока в другой.

Посмотрим на пример реализации:


Соединение = Новый HTTPСоединение("example.com");
Запрос = Новый HTTPЗапрос("/data/export");
Ответ = Соединение.Получить(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    // Получаем входящий поток данных от сервера
    ПотокОтвета = Ответ.ПолучитьТелоКакПоток();
    
    // Создаем поток для записи в файл на диске
    ПутьКФайлу = "D:\Data\export.dat";
    ПотокФайла = Новый ПотокВФайле(ПутьКФайлу, РежимОткрытияФайла.Создать);
    
    // Используем встроенный метод копирования (доступен с 8.3.10)
    // Данный метод автоматически управляет буфером и не переполняет память
    ПотокОтвета.КопироватьВ(ПотокФайла);
    
    // Обязательно закрываем потоки для освобождения ресурсов
    ПотокФайла.Закрыть();
    ПотокОтвета.Закрыть();
    
    Сообщить("Потоковая загрузка завершена.");
КонецЕсли;

Способ 3. Потоковое чтение с использованием буфера (универсальный подход)

Если версия платформы ниже 8.3.10 или требуется обрабатывать данные в процессе чтения (например, для отображения прогресс-бара), мы можем реализовать чтение порциями вручную с помощью объектов ЧтениеДанных и ЗаписьДанных.

Рассмотрим подробнее, как это работает:


ПотокОтвета = Ответ.ПолучитьТелоКакПоток();
Чтение = Новый ЧтениеДанных(ПотокОтвета);

ПотокФайла = Новый ПотокВФайле("C:\temp\result.bin", РежимОткрытияФайла.Создать);
Запись = Новый ЗаписьДанных(ПотокФайла);

// Указываем размер буфера, например, 1 МБ (1024 * 1024 байт)
РазмерБуфера = 1048576; 

Пока Истина Цикл
    // Читаем порцию данных
    ДанныеПорция = Чтение.Прочитать(РазмерБуфера);
    
    // Если прочитано 0 байт - поток закончился
    Если ДанныеПорция.Размер() = 0 Тогда
        Прервать;
    КонецЕсли;
    
    // Записываем порцию в файл
    Запись.Записать(ДанныеПорция);
КонецЦикла;

Запись.Закрыть();
Чтение.Закрыть();

Этот метод позволяет полностью контролировать нагрузку на систему и дает возможность вставить обработчик прерывания пользователем или обновление индикатора прогресса внутри цикла.

Важные нюансы и рекомендации

При работе с потоками следует учитывать несколько критически важных моментов:

Таким образом, использование HTTPОтвет.ПолучитьТелоКакПоток() в сочетании с ПотокВФайле является профессиональным стандартом при разработке интеграций в 1С:Предприятие 8.3, обеспечивая стабильность системы при работе с любыми объемами данных.

← На главную