Как в 1С сохранить файл из интернета, если ссылка на него формируется через некоторое время

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

В практике интеграции 1С с внешними веб-сервисами часто возникает задача скачивания файлов отчетов. Обычно это выглядит просто: мы отправляем GET-запрос и получаем готовый файл. Однако на практике веб-системы (особенно построенные на базе СУБД Oracle, SAP или крупных корпоративных порталов) генерируют тяжелые файлы динамически. Это означает, что при переходе по ссылке файл формируется "на лету" на стороне сервера в течение некоторого времени, а сам запрос требует отправки параметров методом POST.

В данной статье мы подробно разберем, как правильно реализовать скачивание таких файлов в среде «1С:Предприятие 8», избежав типичных ошибок с кодировкой, падением сетевых соединений по таймауту и переполнением оперативной памяти.

Почему стандартный GET-запрос возвращает пустую страницу?

Часто разработчики пытаются скачать файл, просто передавая адрес в метод Получить объекта HTTPСоединение или используя типовые функции БСП. В результате вместо желаемого документа XLS, PDF или ZIP в 1С скачивается HTML-код страницы с параметрами запуска отчета.

Выясним причину этого поведения. Современные веб-системы работают по следующему двухэтапному алгоритму:

  1. Пользователь открывает страницу с формой отчета (это GET-запрос). Сервер возвращает интерфейс выбора параметров.
  2. Пользователь нажимает кнопку запуска (например, «Пуск» или «Сформировать»). Браузер собирает введенные параметры и отправляет на сервер тяжелый POST-запрос с типом содержимого application/x-www-form-urlencoded. Только в ответ на этот POST-запрос сервер начинает генерировать файл и отдает его в виде вложения (заголовок Content-Disposition: attachment).

Если мы в 1С выполняем только первый шаг (GET-запрос), то закономерно получаем лишь стартовую HTML-страницу. Для успешного скачивания файла нам необходимо полностью имитировать поведение браузера на втором шаге.

Шаг 1. Анализируем сетевые запросы через консоль разработчика

Прежде чем писать код в 1С, выполним предварительный анализ. Откроем целевую страницу в браузере Chrome или Яндекс.Браузере, вызовем панель разработчика (клавиша F12) и перейдем на вкладку Network (Сеть). После этого заполним параметры отчета на странице и нажмем кнопку запуска.

Проанализируем зафиксированный запрос. Нам важны следующие заголовки и параметры:

Шаг 2. Решаем проблему с кодированием параметров запроса

При передаче параметров запроса, содержащих кириллицу (например, действие p_action=Пуск), разработчики часто сталкиваются с тем, что сервер не понимает команду. Это происходит из-за того, что в HTTP-протоколе спецсимволы и национальные алфавиты должны передаваться строго в закодированном виде (URL Encoding).

Рассмотрим, как правильно подготовить параметры запроса в 1С:


// Неправильно: передавать кириллицу напрямую
// Параметры = "p_action=Пуск"; 

// Правильно: кодировать спецсимволы и кириллицу
ИмяДействияЗакодировано = КодироватьСтроку("Пуск", СпособКодированияСтроки.URLКодирование);
ПараметрыЗапроса = "rep_user=IVANOV&p_com=30274&p_reestr_format=xls&p_action=" + ИмяДействияЗакодировано;

Если веб-сервер позволяет изменить логику работы, хорошей практикой будет полный отказ от кириллицы в параметрах API (например, замена p_action=Пуск на лаконичное p_action=Start, как и поступил автор темы на форуме).

Шаг 3. Настройка таймаутов для длительных операций

Поскольку файлы отчетов могут формироваться на сервере СУБД Oracle или SAP в течение нескольких минут, наше сетевое соединение должно "уметь" ждать ответа. По умолчанию в 1С таймаут равен 0 (бесконечное ожидание), однако сетевые экраны, прокси-серверы и операционная система могут принудительно разорвать "молчащий" сокет через 30-60 секунд.

Поэтому при создании объекта HTTPСоединение обязательно задаем увеличенный таймаут в секундах (шестой параметр конструктора):


ИмяСервера = "sait2";
Порт = 80;
// Задаем таймаут ожидания в 300 секунд (5 минут)
HTTPСоединение = Новый HTTPСоединение(ИмяСервера, Порт, , , , 300);

Шаг 4. Реализация POST-запроса и потоковое сохранение файла на диск

Если файл отчета имеет большой размер (например, около 90 МБ, как в примере из темы форума), крайне не рекомендуется загружать его целиком в оперативную память сервера 1С с помощью методов вроде ПолучитьТелоКакДвоичныеДанные(). Это может вызвать резкую нехватку ОЗУ и аварийное завершение рабочего процесса rphost.

Правильный подход — использовать потоковую запись ответа сервера непосредственно в файл на жестком диске. Для этого в методе отправки запроса мы вторым параметром передаем путь к локальному файлу.

Разберем готовый программный код реализации этого механизма:


// 1. Определяем пути для сохранения файла
ПутьДляСохранения = "C:\Temp\ПолученныйОтчет.xls";

// 2. Создаем соединение с сервером и настраиваем таймаут
СерверИсточник = "sait2";
HTTPСоединение = Новый HTTPСоединение(СерверИсточник, , , , , 300);

// 3. Формируем тело запроса с параметрами
ПараметрыСтрокой = "rep_user=IVANOV&p_com=30274&p_reestr_format=xls&p_action=Start";

// 4. Настраиваем HTTP-запрос
АдресРесурса = "/pls/info/REESTR_PERSONS_TO_1C.run";
HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрыСтрокой, КодировкаТекста.UTF8);

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

Шаг 5. Работа с сессионными Cookies (если требуется авторизация)

В некоторых случаях веб-сервер не отдает файл по прямому POST-запросу, если предварительно не была инициализирована сессия пользователя. Для этого нам нужно "связать" два запроса: сначала сделать GET-запрос на получение формы (при этом сервер вернет нам Cookie сессии), а затем передать этот Cookie в заголовках нашего POST-запроса.

Посмотрим, как реализовать поддержку сессионности в 1С:


HTTPСоединение = Новый HTTPСоединение("sait2", , , , , 300);

// Шаг А: Отправляем пустой GET-запрос для инициализации сессии
ЗапросИнициализации = Новый HTTPЗапрос("/pls/info/REESTR_PERSONS_TO_1C.run");
ОтветИнициализации = HTTPСоединение.Получить(ЗапросИнициализации);

// Извлекаем Cookie из заголовков ответа
СессионныйКуки = "";
Если ОтветИнициализации.Заголовки.Свойство("Set-Cookie") Тогда
    СессионныйКуки = ОтветИнициализации.Заголовки["Set-Cookie"];
КонецЕсли;

// Шаг Б: Отправляем рабочий POST-запрос с передачей Cookie
РабочийЗапрос = Новый HTTPЗапрос("/pls/info/REESTR_PERSONS_TO_1C.run");
РабочийЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
Если Не ПустаяСтрока(СессионныйКуки) Тогда
    РабочийЗапрос.Заголовки.Вставить("Cookie", СессионныйКуки);
КонецЕсли;

РабочийЗапрос.УстановитьТелоИзСтроки("rep_user=IVANOV&p_com=30274&p_reestr_format=xls&p_action=Start");

// Сохраняем результат
ОтветФайл = HTTPСоединение.ОтправитьДляОбработки(РабочийЗапрос, "C:\Temp\report.xls");

Шаг 6. Динамическое определение оригинального имени файла

В заголовках ответа сервера часто передается оригинальное название генерируемого файла. Например: Content-Disposition: attachment; filename="%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F.xls".

Если вы хотите сохранять файл не под статичным техническим именем, а под тем, которое дал ему веб-сервер, проанализируем заголовки ответа и выполним декодирование:


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

Если ЗначениеЗаполнено(ИмяФайлаСервера) Тогда
    // Теперь мы можем переименовать ранее сохраненный временный файл
    ПереместитьФайл(ПутьДляСохранения, "C:\Temp\" + ИмяФайлаСервера);
КонецЕсли;

Шаг 7. Асинхронное выполнение в современных версиях платформы 1С

Если вы разрабатываете под современные версии платформы «1С:Предприятие 8» (начиная с версии 8.3.21) и скачивание происходит в интерактивном режиме на клиенте, синхронные методы работы с сетью заблокируют интерфейс пользователя на все время ожидания тяжелого отчета. В таких случаях следует использовать асинхронные аналоги методов.

Проанализируем пример асинхронного вызова на клиенте:


&НаКлиенте
Асинхрон Процедура СкачатьФайлАсинхронно()
    
    HTTPСоединение = Новый HTTPСоединение("sait2", , , , , 300);
    HTTPЗапрос = Новый HTTPЗапрос("/pls/info/REESTR_PERSONS_TO_1C.run");
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
    HTTPЗапрос.УстановитьТелоИзСтроки("rep_user=IVANOV&p_com=30274&p_reestr_format=xls&p_action=Start");
    
    ПутьКЛокальномуФайлу = "C:\Temp\report.xls";
    
    Попытка
        // Ожидаем выполнение асинхронного запроса без заморозки интерфейса 1С
        ОтветСервера = Ждать HTTPСоединение.ОтправитьДляОбработкиАсинх(HTTPЗапрос, ПутьКЛокальномуФайлу);
        Если ОтветСервера.КодСостояния = 200 Тогда
            ПоказатьПредупреждение(, "Файл успешно загружен!");
        КонецЕсли;
    Исключение
        ВызватьИсключение "Ошибка при загрузке: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
    
КонецПроцедуры

Применение асинхронных методов повышает общую отзывчивость системы и предотвращает ложные зависания платформы при работе с медленными интернет-ресурсами.

← На главную