В данном разделе описана технология передачи и хранения любых файлов в облаке и их обработка/просмотр из 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 = 'Ошибка'") + ": " + РезультатПередачи;
Сообщить(Ошибка);
КонецЕсли;
КонецЦикла;
КонецПроцедуры