Как загрузить картинку по URL в 1С, исправить ошибку 301 и настроить работу с HTTPS?

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

Загрузка файлов, в частности изображений, из интернета — распространенная задача при интеграции 1С с внешними системами (в чем поможет конструктор API для обмена данными) или просто при заполнении карточек товаров. Однако часто разработчики сталкиваются с ошибками протокола HTTP, такими как "301 Moved Permanently", или проблемами с разбором строки адреса. Рассмотрим подробно, почему возникают эти ошибки, как правильно настроить HTTPСоединение для работы с защищенными ресурсами и какие существуют альтернативные способы получения файлов.

Анализ ошибки 301 и протокола HTTPS

В исходной ситуации мы сталкиваемся с тем, что при попытке выполнить метод Получить() объекта HTTPСоединение возвращается код состояния 301. Давайте разберем, что это значит.

Код ответа 301 Moved Permanently сообщает клиенту, что запрашиваемый ресурс был навсегда перемещен на новый URL. Для диагностики таких ответов удобно использовать инструменты тестирования HTTP сервисов. В современном интернете это чаще всего происходит при принудительном перенаправлении с незащищенного протокола HTTP на защищенный HTTPS. Если мы создаем подключение следующим образом:


СоединениеHTTP = Новый HTTPСоединение("example.com"); // По умолчанию порт 80 (HTTP)

А сервер требует безопасного соединения (порт 443), он вернет ошибку 301 и укажет новый адрес в заголовке Location.

Для решения этой проблемы необходимо явно указать использование защищенного соединения. В 1С для этого используется объект ЗащищенноеСоединениеOpenSSL. Правильная инициализация соединения выглядит так:


// Для работы по HTTPS
СоединениеHTTP = Новый HTTPСоединение(
    "example.com", 
    , // Порт, можно не указывать, если стандартный 443 
    , 
    , 
    , 
    , 
    Новый ЗащищенноеСоединениеOpenSSL()
);

Метод 1: Использование HTTPСоединение с ручным разбором URL

Часто ссылка на картинку хранится в строковом реквизите, и нам нужно программно выделить из неё домен (хост) и путь к ресурсу, а также определить, нужен ли SSL. Рассмотрим алгоритм, который позволяет разобрать строку URL и выполнить запрос.

Разберем рабочий пример, основанный на обсуждении проблемы. Нам необходимо:

  1. Проверить корректность пути.
  2. Разделить URL на части (протокол, домен, путь), применяя проверенные алгоритмы и команды.
  3. Определить, использовать ли защищенное соединение.
  4. Выполнить запрос и сохранить результат.

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


&НаКлиенте
Процедура ЗагрузитьКартинку(Команда)
    ПутьКартинки = Объект.Путь;
    
    // 1. Базовая проверка
    Если ПутьКартинки = "" Тогда
        Сообщить("Путь не указан.");
        Возврат;
    КонецЕсли;

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

    // 3. Определение протокола и настройка SSL
    ЗащищённоеСоединение = Неопределено;
    
    // Проверяем наличие двоеточия в первой части (http: или https:)
    Если Найти(ЧастиПути[0], ":") > 0 Тогда
        // Если протокол https, создаем защищенное соединение
        Если ЧастиПути[0] = "https:" Тогда
            ЗащищённоеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
        КонецЕсли;
        // Удаляем часть с протоколом из массива
        ЧастиПути.Удалить(0);
    Иначе
        // Если протокол не указан явно, но мы предполагаем HTTPS (опционально)
        // В данном примере считаем, что если протокола нет в начале, то сразу идет домен
        // Но лучше всегда рассчитывать на полные ссылки
    КонецЕсли;

    // Теперь первый элемент массива - это домен
    Домен = ЧастиПути[0];
    ЧастиПути.Удалить(0); 
    
    // Собираем оставшиеся части обратно в строку пути ресурса
    ПутьНаСервере = "/" + СтрСоединить(ЧастиПути, "/");

    // 4. Создание соединения и запрос
    // Передаем Домен и параметры защищенного соединения
    СоединениеHTTP = Новый HTTPСоединение(Домен, , , , , , ЗащищённоеСоединение);
    ЗапросHTTP = Новый HTTPЗапрос(ПутьНаСервере);
    
    Попытка
        РезультатЗапроса = СоединениеHTTP.Получить(ЗапросHTTP); 
        
        Если РезультатЗапроса.КодСостояния = 200 Тогда
            // Получаем тело ответа как двоичные данные
            ДанныеКартинки = РезультатЗапроса.ПолучитьТелоКакДвоичныеДанные();
            
            // Создаем объект Картинка
            Картинка = Новый Картинка(ДанныеКартинки);
            
            // Помещаем во временное хранилище для передачи на сервер
            АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(
                Картинка, 
                Новый УникальныйИдентификатор()
            );
            
            ЭтотОбъект.АдресКартинки = АдресВоВременномХранилище;  
        Иначе
            Сообщить("Ошибка загрузки картинки. Код состояния: " + РезультатЗапроса.КодСостояния);
            // Здесь можно добавить логику обработки редиректов (301, 302)
        КонецЕсли;
        
    Исключение
        Сообщить("Ошибка соединения: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

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


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // Проверяем, является ли адрес ссылкой на временное хранилище
    Если ЭтоАдресВременногоХранилища(АдресКартинки) Тогда
        ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресКартинки);
        ТекущийОбъект.Изображение = Новый ХранилищеЗначения(ДвоичныеДанные);
    КонецЕсли;
КонецПроцедуры

Современный подход: Использование СтруктураURI

В примере выше мы разбирали URL вручную, что может привести к ошибкам, если в адресе встретятся нестандартные символы, порты или параметры запроса. Начиная с версии платформы 8.3.21, 1С предоставляет встроенный объект СтруктураURI, который делает это автоматически и надежно.

Давайте посмотрим, как можно упростить код, используя этот объект:


URI = Новый СтруктураURI(ПутьКартинки);
Хост = URI.Хост;
Порт = URI.Порт; // Платформа сама определит порт (80 или 443 или иной)
ПутьНаСервере = URI.ПутьНаСервере; // Включая параметры запроса, если нужно

ЗащищенноеСоединение = Неопределено;
Если URI.Схема = "https" Тогда
    ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
КонецЕсли;

СоединениеHTTP = Новый HTTPСоединение(Хост, Порт, , , , , ЗащищенноеСоединение);

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

Метод 2: Простой способ через КопироватьФайл

Если вам не требуется тонкая настройка заголовков HTTP, работа с cookies или сложная аутентификация, можно воспользоваться системным методом КопироватьФайл. Этот метод использует возможности операционной системы и часто автоматически обрабатывает перенаправления (редиректы 301/302) и HTTPS-соединения, что избавляет нас от написания сложного кода.

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


&НаКлиенте
Процедура ЗагрузитьКартинкуПросто(Команда)
    АдресURL = Объект.Путь;
    
    // Получаем имя временного файла на диске клиента
    ВремФайл = ПолучитьИмяВременногоФайла("jpg");
    
    Попытка
        // Система сама скачает файл по URL и сохранит его во временный путь
        КопироватьФайл(АдресURL, ВремФайл);
        
        // Загружаем сохраненный файл в двоичные данные
        ДвоичныеДанные = Новый ДвоичныеДанные(ВремФайл);
        
        // Помещаем в хранилище (аналогично предыдущему методу)
        АдресВХ = ПоместитьВоВременноеХранилище(ДвоичныеДанные, УникальныйИдентификатор());
        ЭтотОбъект.АдресКартинки = АдресВХ;
        
    Исключение
        Сообщить("Не удалось загрузить картинку: " + ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

Для асинхронной работы (чтобы не блокировать интерфейс при больших файлах или медленном интернете) рекомендуется использовать пару НачатьКопированиеФайла (или КопироватьФайлАсинх) и обработчик оповещения.

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

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

1. Работа через прокси-сервер

В корпоративных сетях прямой доступ в интернет часто закрыт. Если код работает у вас дома, но не работает на сервере заказчика, скорее всего, нужно настроить прокси. В объект HTTPСоединение можно передать параметры прокси:


Прокси = Новый ИнтернетПрокси(Истина); // Использовать системные настройки
СоединениеHTTP = Новый HTTPСоединение(..., Прокси);

2. Заголовки User-Agent

Некоторые сервера защищаются от ботов и блокируют запросы, у которых нет заголовка User-Agent. Они могут возвращать ошибку 403 Forbidden. Чтобы притвориться обычным браузером, добавим заголовок к запросу:


ЗапросHTTP = Новый HTTPЗапрос(ПутьНаСервере);
ЗапросHTTP.Заголовки.Вставить("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");

3. Обработка редиректов (301, 302, 307)

Если вы используете HTTPСоединение, автоматического перехода по редиректам не происходит. Если сервер вернул код 3xx, нужно:

  1. Прочитать заголовок ответа Location.
  2. Извлечь из него новый URL.
  3. Создать новое HTTPСоединение к новому адресу.

Именно поэтому использование КопироватьФайл для простых задач часто бывает предпочтительнее — он делает это за вас.

Заключение

Мы разобрали два основных способа загрузки картинок: гибкий через HTTPСоединение и простой через КопироватьФайл. Для устранения ошибки 301 необходимо либо использовать защищенное соединение (HTTPS), либо позволить системе автоматически обрабатывать перенаправления. Использование современных объектов платформы, таких как СтруктураURI, делает код более чистым и надежным.

← На главную