ООО "Рассвет". Преимущество первых

Перейти к концу метаданных
Переход к началу метаданных

Вы просматриваете старую версию данной страницы. Смотрите текущую версию.

Сравнить с текущим просмотр истории страницы

« Предыдущий Версия 2 Следующий »

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

Зачем использовать S3-хранилища?

Без S3 хранилища:

  • Фото передаются напрямую в базу 1С. Это сильно увеличивает нагрузку на канал от сервера 1С в Интернет, а при нескольких одновременных загрузках канал вообще может быть перегружен и нарушить работу других пользователей, например, работающих через тонкий клиент.

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

  • При отображении картинок в тонком клиенте, их приходится читать из базы и опять забивать канал, а если они еще и не сжаты, то они будут жутко тормозить при отображении на клиенте

Все эти проблемы легко решаются при использовании S3-совместимого хранилища

При использовании хранилища:

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

  • При отправке в S3-хранлище используется мобильный канал от устройства до облака, а ширина каналов у облачных провайдеров значительно выше потребности большинства реальных задач.

  • На сервер приложения 1С отправляется только запись справочника или регистра с ГУИД объекта, отправленного в облако. Это несколько десятков байтов. Соответственно, снижается требование к каналу связи до сервера 1С и собственно, требования к самому серверу

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

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

 

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

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

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

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

  • Нет меток