ООО "Рассвет" для Infostart Event 2021

Object Storage

В данном разделе описана технология передачи и хранения любых файлов в облаке и их обработка/просмотр из 1С

Настройки Облака

Ты можешь использовать любой S3-совместимое хранилище. Я использую Yandex Object Storage

  • Шаг 1. Создай собственное Яндекс.Облако. Воспользуйся ссылкой или qr-кодом, чтобы получить 4000 руб. для экспериментов на свой аккаунт

  • Шаг 2. Создай свой первый каталог и бакет в Object Storage

  • Шаг 3. Создай в бакете папки, в которые будешь складывать файлы

  • Шаг 4. Создай сервисный аккаунт для записи (роли storage.uploader и editor) и аккаунт для чтения (роль viewer)

Настройки выполнены, можно приступать к загрузке фото в Облако

Алгоритм

Общая логика следующая:

  • После того, пользователь, например, сделал фото, создается запись в справочнике Файлы в мобильном устройстве и регистрируется к отправке как в Облако, так и на сервер 1С

  • Фоновая отправка передает двоичные данные в Облако. В Облаке создается объект с именем, равным УИД записи справочника, чтобы потом можно было его найти в миллионах объектов в Облаке

  • Если файл успешно отправлен в Облако, то в записи справочника Файлы можно смело удалять двоичные данные. Так я экономлю место на устройстве пользователя. Файл теперь ему доступен по ссылке.

  • Параллельно на сервер 1С отправляется запись справочника Файлы без двоичных данных. Достаточно знать УИД файла, чтобы найти его в Облаке

Код

После сделанного фото или видео двоичные данные записываются в справочник Файлы в реквизит Данные с типом ХранилищеЗначений и передаются в Яндекс.Облако

Передача двоичных данных в Яндекс.Облако

Функция ПередатьФайлВЯндексОблако(ФайлСсылка, КлассХранилища = "STANDARD") Экспорт УстановитьПривилегированныйРежим(Истина); ДД = ФайлСсылка.Данные.Получить(); Если ДД = Неопределено Тогда Возврат "Нет данных для передачи"; КонецЕсли; СтруктураСоединения = ПолучитьДанныеУчетнойЗаписи("ХраненияФайлов"); //Данные для подключения к Яндекс.Облаку хранятся в зашифрованных данных в ИБ, получаем ввиде структуры СтруктураURI = СтруктураURI(СтруктураСоединения.Сервер); Соединение = Новый HTTPСоединение(СокрЛП(СтруктураURI.Хост)); СтруктураСоединения.Вставить("Хост", СтруктураURI.Хост); ИмяФайла = Строка(ФайлСсылка.УникальныйИдентификатор()) + "." + СокрЛП(ФайлСсылка.Расширение); ОбъектХеш = Новый ХешированиеДанных(ХешФункция.SHA256); ОбъектХеш.Добавить(ДД); ХешСумма = ОбъектХеш.ХешСумма; ХешДД = ПолучитьHexСтрокуИзДвоичныхДанных(ХешСумма); CanonicalQueryString = НРег(ХешДД); CanonicalURI = "/" + СтруктураСоединения.Бакет + "/" + СтруктураСоединения.АдресРесурса + "/" + КодироватьСтроку(ИмяФайла, СпособКодированияСтроки.URLВКодировкеURL); ResourceAddress = "/" + СтруктураСоединения.Бакет + "/" + СтруктураСоединения.АдресРесурса + "/" + КодироватьСтроку(ИмяФайла, СпособКодированияСтроки.URLВКодировкеURL); Данные = СформироватьДанныеДляЗапроса("PUT", ResourceAddress, CanonicalQueryString, CanonicalURI, СтруктураСоединения, КлассХранилища, ФайлСсылка); Если Данные = Неопределено Тогда Возврат "Ошибка формирования данных для запроса, проверьте заполнение настроек."; КонецЕсли; Authorization = СформироватьAuthorization(Данные); Заголовки = Новый Соответствие; Заголовки.Вставить("Authorization", Authorization); Для каждого Строка Из Данные["CanonicalHeaders"] Цикл Заголовки.Вставить(Строка.Ключ, Строка.Значение); КонецЦикла; Заголовки.Вставить("Content-Length", Формат(ДД.Размер(), "ЧГ=")); Если ФайлСсылка.ТипФайла = Перечисления.СР_ТипФайла.Видео Тогда Если ВРег(СокрЛП(ФайлСсылка.Расширение)) = "MOV" Тогда Заголовки.Вставить("Content-Type", "video/quicktime"); Иначе Заголовки.Вставить("Content-Type", "video/" + СокрЛП(ФайлСсылка.Расширение)); КонецЕсли; Иначе Заголовки.Вставить("Content-Type", "image/jpeg"); КонецЕсли; Запрос = Новый HTTPЗапрос(Данные["ResourceAddress"], Заголовки); Запрос.УстановитьТелоИзДвоичныхДанных(ДД); Ответ = Соединение.Записать(Запрос); Если Ответ.КодСостояния = 200 Тогда Попытка Возврат Истина; Исключение Возврат ОписаниеОшибки(); КонецПопытки; Иначе Возврат Ответ.ПолучитьТелоКакСтроку(); КонецЕсли; КонецФункции Функция СформироватьДанныеДляЗапроса(HTTPМетод, ResourceAddress, CanonicalQueryString, CanonicalURI, СтруктураСоединения, КлассХранилища = Неопределено, ФайлСсылка = Неопределено) УстановитьПривилегированныйРежим(Истина); Данные = Новый Соответствие; Данные.Вставить("host", СтруктураСоединения.Хост); Данные.Вставить("Request", HTTPМетод); Данные.Вставить("ResourceAddress", ResourceAddress); Данные.Вставить("AccessKeyID", СтруктураСоединения.ИдентификаторКлюча); Данные.Вставить("Region", "us-east-1"); Данные.Вставить("SecretAccessKey", СтруктураСоединения.СекретныйКлючДоступа); Данные.Вставить("Folder", СтруктураСоединения.АдресРесурса); Данные.Вставить("Algorithm", "AWS4-HMAC-SHA256"); Данные.Вставить("AWSSecretAccessKey", "AWS4" + СтруктураСоединения.СекретныйКлючДоступа); Данные.Вставить("CanonicalQueryString", CanonicalQueryString); Данные.Вставить("CanonicalURI", CanonicalURI); ДатаЗапроса = ТекущаяУниверсальнаяДата(); Данные.Вставить("date", Формат(ДатаЗапроса, "ДФ=yyyyMMdd")); Данные.Вставить("x-amz-date", Формат(ДатаЗапроса, "ДФ=yyyyMMddTHHmmssZ")); // ISO 8601 standard, and must be formatted with the "yyyyMMddTHHmmssZ" Данные.Вставить("expiration-date", Формат(ДатаЗапроса, "ДФ=yyyy-MM-ddTHH:mm:ssZ")); Данные.Вставить("x-amz-credential", Данные["AccessKeyID"] + "/" + Данные["date"] + "/" + Данные["Region"] + "/s3/aws4_request"); Данные.Вставить("x-amz-storage-class", КлассХранилища); Данные.Вставить("x-amz-content-sha256", CanonicalQueryString); ТаблицаЗаголовки = Новый ТаблицаЗначений; ТаблицаЗаголовки.Колонки.Добавить("Заголовок"); ДобавитьЗаголовок(ТаблицаЗаголовки, "x-amz-date"); ДобавитьЗаголовок(ТаблицаЗаголовки, "host"); ДобавитьЗаголовок(ТаблицаЗаголовки, "x-amz-content-sha256"); ТаблицаЗаголовки.Сортировать("Заголовок"); CanonicalHeaders = Новый Соответствие; Для каждого Строка Из ТаблицаЗаголовки Цикл CanonicalHeaders.Вставить(Строка.Заголовок, Данные[Строка.Заголовок]); КонецЦикла; Данные.Вставить("CanonicalHeaders", CanonicalHeaders); Возврат Данные; КонецФункции Функция СформироватьAuthorization(Данные) Authorization = Новый Соответствие; // Алгоритм, который использовался для вычисления подписи. Для AWS Signature Version 4 - AWS4-HMAC-SHA256 Authorization.Вставить("Algorithm", Данные["Algorithm"]); // // Идентификатор ключа доступа и информацию о области видимости Authorization.Вставить("Credential", Данные["AccessKeyID"] + "/" + Данные["date"] + "/" + Данные["Region"] + "/s3/aws4_request"); // Список заголовков запросов, разделенных точкой с запятой, которые используем для вычисления подписи, в нижнем регистре. Authorization.Вставить("SignedHeaders", СформироватьSignedHeaders(Данные["CanonicalHeaders"])); // 256-битная подпись, выраженная в виде 64 строчных шестнадцатеричных символов Authorization.Вставить("Signature", СформироватьSignature(Данные)); СтрокаAuthorization = Authorization["Algorithm"] + " " + "Credential=" + Authorization["Credential"] + "," + "SignedHeaders=" + Authorization["SignedHeaders"] + "," + "Signature=" + Authorization["Signature"]; Возврат СтрокаAuthorization; КонецФункции

Удаление после передачи

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

Процедура ПередатьВсеФайлыВЯндексОблако() ЗапросФайлов = Новый Запрос; ЗапросФайлов.Текст = "ВЫБРАТЬ | Файлы.Ссылка КАК Ссылка |ИЗ | Справочник.Файлы КАК Файлы |ГДЕ | Файлы.МестоХраненияФайла = ЗНАЧЕНИЕ(Перечисление.РВ_МестоХраненияФайла.ВИнформационнойБазе)"; РезультатЗапросаФайлов = ЗапросФайлов.Выполнить(); ВыборкаДетальныеЗаписиФайлов = РезультатЗапросаФайлов.Выбрать(); Пока ВыборкаДетальныеЗаписиФайлов.Следующий() Цикл Попытка РезультатПередачи = ПередатьФайлВЯндексОблако(ВыборкаДетальныеЗаписиФайлов.Ссылка); Исключение РезультатПередачи = ОписаниеОшибки(); КонецПопытки; Если РезультатПередачи = Истина Тогда ФайлОбъект = ВыборкаДетальныеЗаписиФайлов.Ссылка.ПолучитьОбъект(); ФайлОбъект.МестоХраненияФайла = Перечисления.РВ_МестоХраненияФайла.YandexCloud; ФайлОбъект.Данные = Новый ХранилищеЗначения(Неопределено); Попытка ФайлОбъект.Записать(); Исключение Ошибка = НСтр("ru = 'Не удалось изменить место хранения фото'"); Ошибка = Ошибка + ". " + НСтр("ru = 'Ошибка'") + ": " + ОписаниеОшибки(); Сообщить(Ошибка); КонецПопытки; Иначе Ошибка = НСтр("ru = 'Не удалось передать фото в ЯндексОблако'"); Ошибка = Ошибка + ". " + НСтр("ru = 'Ошибка'") + ": " + РезультатПередачи; Сообщить(Ошибка); КонецЕсли; КонецЦикла; КонецПроцедуры

Сжатие и поворот фото

Что еще можно сделать с файлами в Облаке, я описала здесь: сжатие и поворот фото. Триггеры и Cloud Functions

ООО "Рассвет" для Infostart Event 2021