Как настроить выгрузку файлов из 1С в Google Cloud Storage по HTTP API без внешних библиотек

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

Интеграция 1С с облачными хранилищами — частая задача при организации резервного копирования, обмена данными с внешними сайтами или выгрузки отчетов. Одним из наиболее надежных решений является Google Cloud Storage (GCS). Однако разработчики часто сталкиваются с трудностями при настройке авторизации по файлу закрытого ключа keyfile.json и правильном формировании HTTP-запросов к REST API Google без использования внешних компонент и библиотек.

В этой статье мы подробно разберем, как реализовать этот процесс штатными средствами 1С. Мы научимся извлекать данные авторизации, получать временный токен доступа (Bearer token), правильно формировать тело и параметры запроса для загрузки файлов, а также разберем типичные ошибки безопасности и управления правами доступа.

Шаг 1. Разбираемся с авторизацией: как применить keyfile.json

Для взаимодействия с сервисами Google используется сервисный аккаунт. При его создании скачивается файл ключа в формате JSON (обычно с именем вроде keyfile.json). Этот файл содержит конфиденциальные данные, необходимые для генерации токена доступа.

Давайте проанализируем структуру этого файла. Внутри находятся следующие ключевые поля:

Для получения токена доступа (Access Token) напрямую из 1С нам необходимо сформировать так называемый JWT-токен (JSON Web Token), подписать его закрытым ключом по алгоритму RS256 и отправить на сервер авторизации Google. Разберем этот процесс по шагам.

Сначала подготовим структуру JWT-токена. Он состоит из заголовка (Header) и полезной нагрузки (Payload), закодированных в формат Base64URL:


// Пример формирования структуры JWT для обмена на токен доступа
ЗаголовокJWT = "{"alg":"RS256","typ":"JWT"}";

ТекущаяДатаUnix = Формат((ТекущаяДата() - Дата(1970, 1, 1, 0, 0, 0)), "ЧГ=0");
ВремяИстеченияUnix = Формат((ТекущаяДата() + 3600 - Дата(1970, 1, 1, 0, 0, 0)), "ЧГ=0"); // Токен на 1 час

ПолезнаяНагрузка = "{" +
    ""iss":"" + СодержимоеКлюча.client_email + ""," +
    ""scope":"https://www.googleapis.com/auth/devstorage.read_write"," +
    ""aud":"https://oauth2.googleapis.com/token"," +
    ""exp"":" + ВремяИстеченияUnix + "," +
    ""iat"":" + ТекущаяДатаUnix +
"}";

После подготовки данных их необходимо подписать приватным ключом. В 1С для этого можно использовать встроенный объект МенеджерКриптографии или внешние консольные утилиты (например, OpenSSL), установленные на сервере. Подписанная строка отправляется POST-запросом на адрес https://oauth2.googleapis.com/token с параметром grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer. В ответ Google пришлет JSON-структуру, содержащую временный access_token, который мы и будем использовать в качестве Bearer-токена для всех последующих операций.

Шаг 2. Загрузка файла в бакет (Bucket) Google Cloud Storage

Основная сложность, с которой сталкиваются разработчики при работе с GCS API — это правильная передача параметров запроса. Попытка указать имя файла и тип загрузки в заголовках HTTP-запроса приведет к ошибке доступа или неверному имени объекта в облаке.

Выясним причину: согласно спецификации Google Cloud Storage REST API, при простой загрузке (типа media) имя результирующего объекта и тип загрузки должны передаваться строго в строке URL-запроса в качестве GET-параметров, а не в HTTP-заголовках.

Проанализируем правильный шаблон адреса ресурса:

/upload/storage/v1/b/[ИМЯ_БАКЕТА]/o?uploadType=media&name=[ИМЯ_ФАЙЛА_В_ОБЛАКЕ]

Ниже представлен готовый к использованию рабочий пример кода на языке 1С для выгрузки файла в бакет:


// Функция загрузки локального файла в Google Cloud Storage
Функция ЗагрузитьФайлВОблако(ТокенДоступа, ИмяБакета, ПутьКЛокальномуФайлу, ИмяФайлаВОблаке) Экспорт
    
    OpenSSL = Новый ЗащищенноеСоединениеOpenSSL();
    Хост = "storage.googleapis.com";
    
    // Получаем информацию о локальном файле для определения его размера
    ФайлОтправки = Новый Файл(ПутьКЛокальномуФайлу);
    РазмерФайла = XMLСтрока(ФайлОтправки.Размер());
    
    // КРИТИЧЕСКИ ВАЖНО: Имя файла в облаке кодируем для URL, чтобы избежать проблем со спецсимволами
    КодированноеИмяФайла = КодироватьСтроку(ИмяФайлаВОблаке, СпособКодированияСтроки.URL);
    
    // Формируем правильный ресурсный путь с GET-параметрами
    РесурсЗапроса = "/upload/storage/v1/b/" + ИмяБакета + "/o?uploadType=media&name=" + КодированноеИмяФайла;
    
    // Настраиваем заголовки HTTP-запроса
    Заголовки = Новый Соответствие();
    Заголовки.Вставить("Authorization", "Bearer " + ТокенДоступа);
    Заголовки.Вставить("Content-Length", РазмерФайла);
    
    // Указываем корректный MIME-тип для передаваемого контента
    Если НРег(ФайлОтправки.Расширение) = ".csv" Тогда
        Заголовки.Вставить("Content-Type", "text/csv; charset=UTF-8");
    Иначе
        Заголовки.Вставить("Content-Type", "application/octet-stream");
    КонецЕсли;
    
    // Создаем соединение и запрос
    HTTPСоединение = Новый HTTPСоединение(Хост, , , , , , OpenSSL);
    HTTPЗапрос = Новый HTTPЗапрос(РесурсЗапроса, Заголовки);
    
    // Устанавливаем двоичные данные файла непосредственно в тело запроса
    HTTPЗапрос.УстановитьИмяФайлаТела(ПутьКЛокальномуФайлу);
    
    Попытка
        HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
        
        Если HTTPОтвет.КодСостояния = 200 Тогда
            ЗаписьЖурналаРегистрации("GCS.Загрузка", УровеньЖурналаРегистрации.Информация, , , 
                "Файл " + ИмяФайлаВОблаке + " успешно загружен в бакет " + ИмяБакета);
            Возврат Истина;
        ИначеЕсли HTTPОтвет.КодСостояния = 401 Тогда
            ВызватьИсключение "Ошибка авторизации: Истек или не принят OAuth-токен.";
        Иначе
            ВызватьИсключение "Ошибка загрузки! Код состояния: " + HTTPОтвет.КодСостояния + 
                ". Ответ сервера: " + HTTPОтвет.ПолучитьТелоКакСтроку();
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("GCS.Ошибка", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат Ложь;
    КонецПопытки;
    
КонецФункции

Шаг 3. Решение проблемы с ошибкой "403 Insufficient Permission"

Если при попытке выполнить запрос 1С возвращает ошибку 403 Forbidden с диагностическим сообщением "Insufficient Permission" (недостаточно прав), необходимо проверить настройки безопасности на стороне Google Cloud Platform Console.

Для исправления этой проблемы выполним следующие действия:

  1. Перейдем в консоль управления Google Cloud Console в раздел IAM & Admin -> IAM.
  2. Найдем в списке наш Сервисный аккаунт (его email указан в поле client_email файла keyfile.json).
  3. Проверим роли, назначенные этому аккаунту. Для успешного создания объектов в бакете ему необходима как минимум роль Storage Object Creator (Создатель объектов хранилища) или более широкая — Storage Object Admin (Администратор объектов хранилища).
  4. Если роли настроены на уровне проекта, но доступ все равно заблокирован, откроем настройки конкретного бакета в разделе Cloud Storage -> Buckets, перейдем на вкладку Permissions (Разрешения) и добавим наш Сервисный аккаунт в список субъектов с ролью Storage Object Admin непосредственно для этого хранилища.

Шаг 4. Управление правами доступа и публикация файлов

Часто бизнес-процесс требует, чтобы загруженный файл был доступен для скачивания по прямой ссылке без авторизации (например, для интеграции с маркетплейсами или CRM). Попытка сделать это путем вызова метода setIamPolicy на весь бакет (как показано в сообщении №42) является крайне опасной операцией.

Внимание! Вызов setIamPolicy через метод PUT полностью перезаписывает всю политику прав доступа бакета. Если отправить туда JSON-структуру, содержащую только роль чтения для всех пользователей (allUsers), вы удалите права доступа для всех остальных учетных записей, включая администраторов проекта и саму систему 1С!

Чтобы безопасно предоставить публичный доступ только к конкретному загруженному файлу, рассмотрим использование механизмов **Object ACL** (списков управления доступом на уровне объекта). Разберем по шагам, как это реализовать:


// Процедура делает публичным конкретный объект в бакете GCS
ПроцедураСделатьОбъектПубличным(ТокенДоступа, ИмяБакета, ИмяФайлаВОблаке)
    
    OpenSSL = Новый ЗащищенноеСоединениеOpenSSL();
    Хост = "storage.googleapis.com";
    
    // Кодируем имя объекта для формирования валидного URI
    КодированноеИмяФайла = КодироватьСтроку(ИмяФайлаВОблаке, СпособКодированияСтроки.URL);
    РесурсЗапроса = "/storage/v1/b/" + ИмяБакета + "/o/" + КодированноеИмяФайла + "/acl";
    
    // Тело запроса, определяющее публичный доступ на чтение (READER) для всех пользователей (allUsers)
    ТелоJSON = "{
    |   ""entity"": ""allUsers"",
    |   ""role"": ""READER""
    |}";
    
    Заголовки = Новый Соответствие();
    Заголовки.Вставить("Authorization", "Bearer " + ТокенДоступа);
    Заголовки.Вставить("Content-Type", "application/json; charset=UTF-8");
    
    HTTPСоединение = Новый HTTPСоединение(Хост, , , , , , OpenSSL);
    HTTPЗапрос = Новый HTTPЗапрос(РесурсЗапроса, Заголовки);
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    Попытка
        // Для добавления правила в ACL используется метод POST
        HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
        
        Если HTTPОтвет.КодСостояния = 200 Тогда
            Сообщить("Файл успешно опубликован. Ссылка для скачивания: ");
            Сообщить("https://storage.googleapis.com/" + ИмяБакета + "/" + ИмяФайлаВОблаке);
        Иначе
            ВызватьИсключение HTTPОтвет.ПолучитьТелоКакСтроку();
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("GCS.ОшибкаПубликации", УровеньЖурналаРегистрации.Ошибка, , , 
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
    
КонецПроцедуры

Шаг 5. Особенности разработки на современных версиях платформы 1С

При реализации обмена с внешними API важно учитывать особенности выполнения кода на платформе 1С:Предприятие:

Используя предложенные шаблоны кода и следуя правилам конфигурирования прав доступа в GCP, мы можем надежно связать информационную базу 1С с облачным хранилищем Google Cloud Storage, обеспечив высокую скорость передачи и безопасность обмена данными.

← На главную