В данном разделе описана технология передачи и хранения любых файлов в облаке и их обработка/просмотр из 1С
Настройки Облака
Ты можешь использовать любое S3-совместимое хранилище. Я использую Yandex Object Storage
Шаг 1. Создай собственное Яндекс.Облако. Воспользуйся ссылкой или qr-кодом, чтобы получить 4000 руб. для экспериментов на свой аккаунт
Шаг 2. Создай свой первый каталог и бакет в Object Storage
Шаг 3. Создай в бакете папки, в которые будешь складывать файлы
Шаг 4. Создай сервисный аккаунт для записи (роли storage.uploader и editor) и аккаунт для чтения (роль viewer)
Кликни для регистрации личного Облака
Настройки выполнены, можно приступать к загрузке фото в Облако
Алгоритм
Общая логика следующая:
После того, как пользователь сделал фото, создается запись в справочнике Файлы в мобильном устройстве и эта запись регистрируется к отправке как в Облако (S3-хранилище), так и на сервер 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 = 'Ошибка'") + ": " + РезультатПередачи;
Сообщить(Ошибка);
КонецЕсли;
КонецЦикла;
КонецПроцедуры