Загрузка файла по URL во встроенном языке 1С: HTTPСоединение, потоки, авторизация

· 11 мин

Содержание

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

Звучит просто, но на практике ссылка отдаёт редирект, сайт требует токен, сервер 1С стоит за прокси, а JSON приходит в неожиданной кодировке. Ниже готовая функция и разбор каждого такого случая. Всё это относится к платформе 8.3 в целом и не зависит от конфигурации: код одинаково работает в БП, ЗУП, УТ и в самописных базах.

Как 1С ходит в интернет: объекты и место выполнения

Загрузка по ссылке собирается из трёх объектов встроенного языка:

  • 🌐 HTTPСоединение — подключение к конкретному серверу: имя хоста, порт, логин и пароль, прокси, таймаут, параметры TLS.
  • 📨 HTTPЗапрос — что именно просим: путь к ресурсу на сервере и заголовки.
  • 📦 HTTPОтвет — что вернулось: КодСостояния, Заголовки и тело, которое читают как строку, двоичные данные, поток или файл.

Главное отличие от браузера: соединение создаётся не по полной ссылке, а по имени хоста. Строку https://example.ru/files/price.xlsx нужно разрезать на сервер example.ru и ресурс /files/price.xlsx. Схема определяет порт и шифрование: https — порт 443 и объект ЗащищенноеСоединениеOpenSSL, http — порт 80 без него.

Второй вопрос — где выполняется код. Лучше всего скачивать на сервере 1С: там есть доступ к файловой системе, а в веб-клиенте его нет. Отсюда следствие, о котором забывают при переносе базы: в клиент-серверном варианте в интернет выходит не компьютер пользователя, а сервер 1С. Если у сервера нет выхода наружу или нужен прокси, код, который работал в файловой базе, перестанет скачивать.

В конфигурациях на БСП уже есть готовый механизм — общий модуль ПолучениеФайловИзИнтернета с функцией СкачатьФайлНаСервере(URL, ПараметрыПолучения). Он сам разбирает ссылку, учитывает настройки прокси из программы и возвращает структуру с полями Статус, Путь, СообщениеОбОшибке. Если пишете обработку для типовой конфигурации, начните с него.

Скачиваем файл по ссылке через HTTPСоединение: готовая функция

Функция ниже работает без БСП. Она принимает полную ссылку, скачивает файл во временный каталог сервера и возвращает путь к нему. Размещайте её в общем модуле с флажком «Сервер» или в модуле объекта обработки.

Функция СкачатьФайлПоСсылке(Знач URL, Знач ИмяФайла = "") Экспорт

    // 1. Разбираем ссылку на схему, хост, порт и путь
    Схема = "http";
    Позиция = СтрНайти(URL, "://");
    Если Позиция > 0 Тогда
        Схема = НРег(Лев(URL, Позиция - 1));
        URL = Сред(URL, Позиция + 3);
    КонецЕсли;

    Позиция = СтрНайти(URL, "/");
    Если Позиция > 0 Тогда
        Хост = Лев(URL, Позиция - 1);
        Ресурс = Сред(URL, Позиция);
    Иначе
        Хост = URL;
        Ресурс = "/";
    КонецЕсли;

    Порт = ?(Схема = "https", 443, 80);
    Позиция = СтрНайти(Хост, ":");
    Если Позиция > 0 Тогда
        Порт = Число(Сред(Хост, Позиция + 1));
        Хост = Лев(Хост, Позиция - 1);
    КонецЕсли;

    // 2. Соединение: таймаут 60 секунд, TLS только для https
    Защищенное = ?(Схема = "https", Новый ЗащищенноеСоединениеOpenSSL(), Неопределено);
    Соединение = Новый HTTPСоединение(Хост, Порт, , , , 60, Защищенное);

    // 3. Запрос с заголовками
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("User-Agent", "1C-Enterprise-8.3");
    Запрос = Новый HTTPЗапрос(Ресурс, Заголовки);

    // 4. Тело ответа пишем сразу в файл
    Если ПустаяСтрока(ИмяФайла) Тогда
        ИмяФайла = ПолучитьИмяВременногоФайла();
    КонецЕсли;
    Ответ = Соединение.Получить(Запрос, ИмяФайла);

    // 5. Проверяем результат
    Если Ответ.КодСостояния <> 200 Тогда
        УдалитьФайлы(ИмяФайла);
        ВызватьИсключение СтрШаблон("Файл не получен, сервер вернул код %1", Ответ.КодСостояния);
    КонецЕсли;

    Возврат ИмяФайла;

КонецФункции

Пример вызова: ПутьКФайлу = СкачатьФайлПоСсылке("https://example.ru/files/price.xlsx", ПолучитьИмяВременногоФайла("xlsx")). Расширение важно, если дальше файл открывают по типу, например табличным документом.

Когда ссылка ведёт на редирект

Платформенный HTTPСоединение не переходит по перенаправлениям сам. Если сервер ответил кодом 301, 302, 303, 307 или 308, в файле окажется короткая HTML-страница, а новый адрес лежит в заголовке Location. Так ведут себя облачные хранилища, короткие ссылки и сайты, которые переводят http на https.

Решение — цикл на несколько переходов:

  1. Выполните запрос и прочитайте КодСостояния.
  2. Если код из списка выше, возьмите значение Location и повторите запрос уже по нему.
  3. Если адрес относительный (начинается с /), подставьте к нему схему и хост предыдущего запроса.
  4. Ограничьте число переходов, например пятью, чтобы не зациклиться.

Имя заголовка в Ответ.Заголовки может прийти как Location, так и location. Ищите его без учёта регистра:

Функция ЗначениеЗаголовка(Заголовки, Знач Имя)
    Имя = НРег(Имя);
    Для Каждого Элемент Из Заголовки Цикл
        Если НРег(Элемент.Ключ) = Имя Тогда
            Возврат Элемент.Значение;
        КонецЕсли;
    КонецЦикла;
    Возврат "";
КонецФункции

Как узнать настоящее имя файла

Ссылка вида /download?id=4512 не говорит, что за файл придёт. Имя часто передают в заголовке Content-Disposition: attachment; filename="act_0915.pdf". Достаньте его функцией ЗначениеЗаголовка и вырежьте часть после filename=. Тип содержимого подскажет Content-Type: application/pdf, application/zip, text/xml.

Большие файлы: запись на диск и потоковое чтение

Самая частая ошибка с большими файлами — читать тело в память. Вызов Ответ.ПолучитьТелоКакДвоичныеДанные() для архива в 700 МБ держит в памяти рабочего процесса весь архив, а при параллельной работе нескольких пользователей это заметно бьёт по серверу.

Три способа, от лёгкого к тяжёлому:

СпособЧто происходитКогда подходит
Получить(Запрос, ИмяФайла)Тело пишется на диск по мере полученияЛюбой файл, особенно больше 50–100 МБ
Ответ.ПолучитьТелоКакПоток()Тело читается порциями через потокНужно обрабатывать данные на лету, не сохраняя файл
Ответ.ПолучитьТелоКакДвоичныеДанные()Весь ответ в памятиНебольшие файлы: картинки, PDF, ответы API

После записи на диск файл часто нужно положить в базу или отдать пользователю. Для базы читайте его в Новый ДвоичныеДанные(ИмяФайла) и помещайте в ХранилищеЗначения. Для пользователя — ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяФайла), УникальныйИдентификатор). Адрес передайте на клиент и сохраните файл через ФайловаяСистемаКлиент.СохранитьФайл (в конфигурациях на БСП).

Временный файл после использования удаляйте вызовом УдалитьФайлы(ИмяФайла). Каталог временных файлов сервера сам не чистится, и за месяц ежедневной загрузки прайсов там накапливаются гигабайты.

Докачка и обрыв соединения

Для очень больших файлов поможет заголовок Range: bytes=524288000- просит отдать файл начиная с 500-го мегабайта. Если сервер поддерживает докачку, он ответит кодом 206, и остаток можно дописать к уже скачанной части. Код 200 на такой запрос значит, что докачки нет и файл придёт заново целиком.

Таймаут в конструкторе задаётся в секундах. Ноль означает «ждать бесконечно», и при зависшем сервере фоновое задание висит часами. Ставьте конкретное число: 30–60 секунд для API, 300–600 для тяжёлых файлов.

Авторизация, токены, заголовки и прокси

Файлы из личных кабинетов, API маркетплейсов и корпоративных хранилищ закрыты паролем. Способ передачи зависит от того, что ждёт сервер.

Базовая авторизация (логин и пароль). Проще всего передать их в конструктор: Новый HTTPСоединение(Хост, Порт, "user", "secret", , 60, Защищенное). Если сервер не присылает запрос на авторизацию, а сразу ждёт заголовок, сформируйте его сами:

Строка64 = Base64Строка(ПолучитьДвоичныеДанныеИзСтроки("user:secret"));
Строка64 = СтрЗаменить(СтрЗаменить(Строка64, Символы.ВК, ""), Символы.ПС, "");
Заголовки.Вставить("Authorization", "Basic " + Строка64);

Замена переносов не лишняя: Base64Строка разбивает длинный результат на строки, и заголовок с переносом сервер отвергнет.

Токен. API обычно ждёт Authorization: Bearer <токен> или собственный заголовок вроде X-API-Key. Добавьте его в соответствие Заголовки до создания HTTPЗапрос.

Прочие заголовки, которые помогают, когда сервер капризничает:

  • 🧾 User-Agent — часть сайтов отдаёт 403 запросам без него;
  • 📄 Accept — application/json или application/xml, если API умеет отдавать оба формата;
  • 🗜️ Accept-Encoding — не добавляйте его без нужды, иначе можете получить сжатое тело, которое придётся распаковывать самому.

Не храните пароли и токены в тексте модуля. Код конфигурации видят все, у кого есть доступ к Конфигуратору, а выгрузки уходят подрядчикам. Держите секреты в константе или регистре сведений с ограниченными правами, в БСП — в безопасном хранилище данных.

Прокси. Если сервер 1С выходит в интернет через прокси, передайте объект ИнтернетПрокси пятым параметром. Новый ИнтернетПрокси(Истина) берёт системные настройки той учётной записи, под которой работает служба 1С. У службы она обычно своя, и настроек браузера администратора она не видит. Поэтому надёжнее задать прокси явно: создайте Новый ИнтернетПрокси, вызовите Установить("https", "proxy.local", 3128) и заполните свойства Пользователь и Пароль. В БСП готовый объект вернёт ПолучениеФайловИзИнтернета.ПолучитьПрокси(URL) с учётом настроек из программы.

Кодировки и форматы: как прочитать текст, JSON и XML

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

Метод Ответ.ПолучитьТелоКакСтроку() без параметра берёт кодировку из заголовка Content-Type (часть charset=). Если заголовка нет или он врёт, укажите кодировку явно:

  • ПолучитьТелоКакСтроку(КодировкаТекста.UTF8) — для большинства современных API;
  • ПолучитьТелоКакСтроку("windows-1251") — для старых российских сайтов и выгрузок из учётных систем.

Если файл уже сохранён на диск, тот же выбор делается при чтении: Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8).

Разбор JSON

Текст = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Текст);
Данные = ПрочитатьJSON(Чтение, Истина);
Чтение.Закрыть();

Второй параметр Истина возвращает Соответствие вместо Структура. Так безопаснее: ключи JSON с дефисами или цифрой в начале не станут ключами структуры и уронят разбор. Если в начале текста стоит невидимый символ BOM, ошибка разбора появится уже на первой позиции. Тогда читайте файл с диска через ЧтениеJSON.ОткрытьФайл(ИмяФайла) или уберите первый символ строки.

Разбор XML

Для XML удобнее читать прямо из скачанного файла, не загружая строку целиком:

Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяФайла);
Значения = Новый Массив;
Пока Чтение.Прочитать() Цикл
    Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "Name" Тогда
        Чтение.Прочитать();
        Если Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
            Значения.Добавить(Чтение.Значение);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;
Чтение.Закрыть();

Кодировку XML объявляет сам: в первой строке <?xml version="1.0" encoding="windows-1251"?>. ЧтениеXML учитывает это объявление, поэтому XML надёжнее разбирать из файла, чем из строки, прочитанной с угаданной кодировкой.

Какой способ получения файла выбрать

СпособПлюсыМинусы
HTTPСоединение.Получить с именем файлаРаботает в любой конфигурации, полный контроль над заголовкамиРедиректы и разбор ссылки пишете сами
ПолучениеФайловИзИнтернета.СкачатьФайлНаСервере (БСП)Разбирает URL, учитывает прокси из настроек программы, возвращает понятную ошибкуТолько в конфигурациях на БСП
HTTPСоединение.ОтправитьДляОбработки или ВызватьHTTPМетодНужен, когда файл отдают только на POST-запрос с параметрами в телеТело запроса формируете вручную
FTPСоединениеДля ссылок вида ftp://Другой протокол, свой набор методов
Запуск внешней утилиты (curl, wget)Знакома администраторамЗависит от ОС сервера, ошибки сложно поймать в коде, лишнее право на запуск программ

Для разовой обработки в типовой базе берите БСП. Для регламентного задания, интеграции с API или самописной конфигурации надёжнее собственная функция на HTTPСоединение: вы точно знаете, какие заголовки уходят и что делать с каждым кодом ответа.

Ошибки при загрузке файла по URL и что с ними делать

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

Диагностика: выберите симптом

Ещё две ситуации без явного текста ошибки:

  • 🔒 Код в расширении или внешней обработке падает на создании HTTPСоединение. Причина — безопасный режим: расширения и дополнительные обработки, работающие в нём, не могут выходить в интернет, пока это не разрешит профиль безопасности или настройка расширения.
  • 📉 Скачанный файл есть, но меньше ожидаемого и не открывается. Скорее всего, вместо файла сервер вернул страницу с ошибкой или капчей. Откройте его в текстовом редакторе: первые строки всё объяснят.

Частые вопросы

Можно ли скачать файл на клиенте, а не на сервере?
Можно в тонком клиенте под Windows: HTTPСоединение доступен и на клиенте. Но в веб-клиенте прямого доступа к файлам нет, поэтому универсальный вариант — скачать на сервере и передать файл через временное хранилище.
Как кириллица и пробелы в ссылке влияют на загрузку?
Путь нужно кодировать: КодироватьСтроку(Путь, СпособКодированияСтроки.URLВКодировкеURL). Без этого сервер может ответить 404 или 400, хотя в браузере ссылка открывается.
Как скачивать файл по расписанию?
Вызовите функцию из процедуры регламентного задания и задайте расписание в его свойствах. Учтите, что задание работает на сервере под учётной записью службы 1С, поэтому прокси и сертификаты проверяйте именно для неё.
Почему в файловой базе всё качается, а на сервере 1С нет?
В файловой базе запрос идёт с компьютера пользователя, в клиент-серверной — с сервера 1С. У сервера может не быть выхода в интернет, своего DNS или настроек прокси.
Как проверить размер файла, не скачивая его?
Отправьте запрос методом Соединение.ПолучитьЗаголовки(Запрос): сервер вернёт только заголовки. Размер в байтах лежит в Content-Length, если сервер его передаёт.