1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2024-11-21 17:56:56 +02:00

S3: Получение файла и получение файла по частям

This commit is contained in:
Anton Titovets 2024-11-19 14:40:35 +03:00
parent 1d0cf5a0a8
commit de4e81ab78
10 changed files with 7770 additions and 7421 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1009,7 +1009,7 @@
ДополнитьКомплексныйАтрибут(ОбъектВидео, 21841, 100001, СтруктураURL);
ДополнитьКомплексныйАтрибут(ОбъектВидео, 21837, 100001, СтруктураНаименования);
Если Не OPI_Инструменты.ПолеКоллекцииСуществет(СтруктураТовара, Complex_attributes_) Тогда
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(СтруктураТовара, Complex_attributes_) Тогда
СтруктураТовара.Вставить(Complex_attributes_, Новый Массив);
КонецЕсли;
@ -1044,7 +1044,7 @@
ДополнитьКомплексныйАтрибут(ОбъектОбложка, 21845, 100002, СтруктураURL);
Если Не OPI_Инструменты.ПолеКоллекцииСуществет(СтруктураТовара, Complex_attributes_) Тогда
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(СтруктураТовара, Complex_attributes_) Тогда
СтруктураТовара.Вставить(Complex_attributes_, Новый Массив);
КонецЕсли;
@ -1078,7 +1078,7 @@
СтруктураАтрибута = Новый Структура("id,complex_id,values", IDАтрибута, КомплексныйID, Значения);
Если Не OPI_Инструменты.ПолеКоллекцииСуществет(Коллекция, Attributes_) Тогда
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(Коллекция, Attributes_) Тогда
Коллекция.Вставить(Attributes_, Новый Массив);
КонецЕсли;

View File

@ -75,15 +75,19 @@
// Отправляет простой http запрос без тела
//
// Параметры:
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// ОжидаютсяДвоичные - Булево - Отключает попытку преобразования ответа в JSON - binary
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - сериализованный JSON ответа от хранилища
Функция ОтправитьЗапросБезТела(Знач Метод, Знач ОсновныеДанные, Знач Заголовки = Неопределено) Экспорт
Функция ОтправитьЗапросБезТела(Знач Метод
, Знач ОсновныеДанные
, Знач ОжидаютсяДвоичные = Ложь
, Знач Заголовки = Неопределено) Экспорт
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, , Заголовки);
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, , ОжидаютсяДвоичные, Заголовки);
Возврат Ответ;
КонецФункции
@ -92,16 +96,21 @@
// Отправляет http запрос с телом
//
// Параметры:
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// Тело - Строка, ДвоичныеДанные - Двоичное тело запроса или путь к файлу - body
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// Тело - Строка, ДвоичныеДанные - Двоичное тело запроса или путь к файлу - body
// ОжидаютсяДвоичные - Булево - Отключает попытку преобразования ответа в JSON - binary
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - сериализованный JSON ответа от хранилища
Функция ОтправитьЗапросСТелом(Знач Метод, Знач ОсновныеДанные, Знач Тело, Знач Заголовки = Неопределено) Экспорт
Функция ОтправитьЗапросСТелом(Знач Метод
, Знач ОсновныеДанные
, Знач Тело
, Знач ОжидаютсяДвоичные = Ложь
, Знач Заголовки = Неопределено) Экспорт
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, Тело, Заголовки);
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, Тело, ОжидаютсяДвоичные, Заголовки);
Возврат Ответ;
КонецФункции
@ -216,7 +225,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, XMLКонфигурация, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, XMLКонфигурация, , Заголовки);
Возврат Ответ;
@ -248,7 +257,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -280,7 +289,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -318,7 +327,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, , Заголовки);
Возврат Ответ;
@ -349,7 +358,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -380,7 +389,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -419,7 +428,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, , Заголовки);
Возврат Ответ;
@ -450,7 +459,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -488,7 +497,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -527,7 +536,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапрос("PUT", ОсновныеДанные, Содержимое, Заголовки);
Ответ = ОтправитьЗапрос("PUT", ОсновныеДанные, Содержимое, , Заголовки);
Возврат Ответ;
@ -570,13 +579,68 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("HEAD", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("HEAD", ОсновныеДанные, , Заголовки);
Ответ["response"] = Новый Структура;
Возврат Ответ;
КонецФункции
// Получить объект
// Получает содержимое объекта из бакета
//
// Примечание:
// Метод в документации AWS: [GetObjectAttributes](@docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html)
// Вы можете использовать поле `ChunkSize` в основных данных для указания минимального размера и величины чанка загрузки по частям
// Например, ChunkSize=X означает, что все файлы, размером больше X (в байтах) будут загружаться по частям, где одна часть будет размером X.
// Загрузка по частям используется для больших файлов. Стандартный размер ChunkSize - 20000000 байт (20 МБайт)
//
// Параметры:
// Наименование - Строка - Наименование объекта в бакете - name
// Бакет - Строка - Имя бакета, в котором находится объект - bucket
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса. См. ПолучитьСтруктуруДанных - basic
// Каталог - Булево - Истина > Directory Bucket, Ложь > General Purpose Bucket - dir
// Версия - Строка - Токен для получения конкретной версии объекта - ver
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
// ПутьСохранения - Строка - Путь для прямой записи файла на диск - out
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - сериализованный JSON ответа от хранилища
Функция ПолучитьОбъект(Знач Наименование
, Знач Бакет
, Знач ОсновныеДанные
, Знач Версия = ""
, Знач Заголовки = Неопределено
, Знач ПутьСохранения = "") Экспорт
Если OPI_Инструменты.ПолеКоллекцииСуществует(ОсновныеДанные, "ChunkSize") Тогда
МаксимальныйРазмер = ОсновныеДанные["ChunkSize"];
Иначе
МаксимальныйРазмер = 20000000;
КонецЕсли;
ИнформацияОбъекта = ПолучитьОписаниеОбъекта(Наименование, Бакет, ОсновныеДанные, Версия);
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(ИнформацияОбъекта, "headers.Content-Length") Тогда
Возврат ИнформацияОбъекта;
КонецЕсли;
РазмерОбъекта = ИнформацияОбъекта["headers"]["Content-Length"];
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
OPI_ПреобразованиеТипов.ПолучитьЧисло(РазмерОбъекта);
Если РазмерОбъекта > МаксимальныйРазмер Тогда
Размеры = Новый Структура("object,chunk", РазмерОбъекта, МаксимальныйРазмер);
Ответ = ПолучитьОбъектЧастями(ОсновныеДанные, Заголовки, ПутьСохранения, Размеры);
Иначе
Ответ = ПолучитьОбъектЦеликом(ОсновныеДанные, Заголовки, ПутьСохранения);
КонецЕсли;
Возврат Ответ;
КонецФункции
// Удалить объект
// Удаляет объект из бакета
//
@ -613,7 +677,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -692,7 +756,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, , Заголовки);
Возврат Ответ;
@ -732,7 +796,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -772,7 +836,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -811,7 +875,7 @@
URL = URL + OPI_Инструменты.ПараметрыЗапросаВСтроку(Параметры);
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -850,7 +914,7 @@
URL = URL + OPI_Инструменты.ПараметрыЗапросаВСтроку(Параметры, , Ложь);
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -1140,7 +1204,11 @@
#Область Прочее
Функция ОтправитьЗапрос(Знач Метод, Знач ОсновныеДанные, Знач Тело = Неопределено, Знач Заголовки = Неопределено)
Функция ОтправитьЗапрос(Знач Метод
, Знач ОсновныеДанные
, Знач Тело = Неопределено
, Знач ОжидаютсяДвоичные = Ложь
, Знач Заголовки = Неопределено)
ПроверитьОсновныеДанные(ОсновныеДанные);
@ -1164,7 +1232,7 @@
Запрос.Заголовки.Вставить("Authorization", ЗаголовокАвторизации);
Ответ = OPI_Инструменты.ВыполнитьЗапрос(Запрос, Соединение, Метод, , Истина);
Ответ = ОформитьОтвет(Ответ);
Ответ = ОформитьОтвет(Ответ, ОжидаютсяДвоичные);
Возврат Ответ;
@ -1177,12 +1245,102 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела(Метод, ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела(Метод, ОсновныеДанные, , Заголовки);
Возврат Ответ;
КонецФункции
Функция ПолучитьОбъектЧастями(Знач ОсновныеДанные
, Знач Заголовки
, Знач ПутьСохранения
, Знач Размеры)
ОбщийРазмер = Размеры["object"];
РазмерУчастка = Размеры["chunk"];
ШаблонЗаголовка = "bytes=%1-%2";
НачалоУчастка = 0;
МассивЗаголовков = Новый Массив;
Пока НачалоУчастка < ОбщийРазмер - РазмерУчастка Цикл
КонецУчастка = НачалоУчастка + РазмерУчастка - 1;
НачалоСтр = OPI_Инструменты.ЧислоВСтроку(НачалоУчастка);
КонецСтр = OPI_Инструменты.ЧислоВСтроку(КонецУчастка);
Заголовок = СтрШаблон(ШаблонЗаголовка, НачалоСтр, КонецСтр);
МассивЗаголовков.Добавить(Новый Структура("Заголовок,Позиция", Заголовок, КонецСтр));
НачалоУчастка = КонецУчастка + 1;
КонецЦикла;
Если Не НачалоУчастка = ОбщийРазмер Тогда
Заголовок = "bytes=" + OPI_Инструменты.ЧислоВСтроку(НачалоУчастка) + "-";
СтруктураИнформации = Новый Структура("Заголовок,Позиция", Заголовок, ОбщийРазмер);
МассивЗаголовков.Добавить(СтруктураИнформации);
КонецЕсли;
Если ЗначениеЗаполнено(ПутьСохранения) Тогда
ПотокФайла = Новый ФайловыйПоток(ПутьСохранения, РежимОткрытияФайла.Создать);
Иначе
ПотокФайла = Новый ПотокВПамяти();
КонецЕсли;
ЗаписьФайла = Новый ЗаписьДанных(ПотокФайла);
Для Каждого ТекущийНабор Из МассивЗаголовков Цикл
ЗаголовокУчастка = ТекущийНабор["Заголовок"];
ТекущаяПозиция = ТекущийНабор["Позиция"];
ЗаголовокИсточника = Новый Соответствие();
ЗаголовокИсточника.Вставить("Range", ЗаголовокУчастка);
ДобавитьДополнительныеЗаголовки(Заголовки, ЗаголовокИсточника);
ПромежуточныйРезультат = ПолучитьОбъектЦеликом(ОсновныеДанные, ЗаголовокИсточника);
ЗаписьФайла.Записать(ПромежуточныйРезультат);
КБайт = 1024;
МБайт = КБайт * КБайт;
Сообщить(OPI_Инструменты.ИнформацияОПрогрессе(ТекущаяПозиция, ОбщийРазмер, "МБ", МБайт));
ВыполнитьСборкуМусора();
ОсвободитьОбъект(ПромежуточныйРезультат);
КонецЦикла;
ЗаписьФайла.Закрыть();
Если ТипЗнч(ПотокФайла) = Тип("ПотокВПамяти") Тогда
Возврат ПотокФайла.ЗакрытьИПолучитьДвоичныеДанные();
Иначе
ПотокФайла.Закрыть();
Возврат Новый Структура("file", ПутьСохранения);
КонецЕсли;
КонецФункции
Функция ПолучитьОбъектЦеликом(Знач ОсновныеДанные
, Знач Заголовки
, Знач ПутьСохранения = "")
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Истина, Заголовки);
Если ЗначениеЗаполнено(ПутьСохранения) Тогда
Ответ.Записать(ПутьСохранения);
Возврат Новый Структура("file", ПутьСохранения);
Иначе
Возврат Ответ;
КонецЕсли;
КонецФункции
Функция ОформитьОтвет(Знач Ответ, Знач ОжидаютсяДвоичные = Ложь)
Статус = Ответ.КодСостояния;

View File

@ -2155,6 +2155,7 @@
S3_УдалитьТегиОбъекта(ПараметрыТеста);
S3_ПолучитьСписокОбъектов(ПараметрыТеста);
S3_ПолучитьСписокВерсийОбъектов(ПараметрыТеста);
S3_ПолучитьОбъект(ПараметрыТеста);
S3_УдалитьОбъект(ПараметрыТеста);
S3_УдалитьБакет(ПараметрыТеста);
@ -15053,6 +15054,16 @@
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ЗагрузитьОбъект", "S3");
OPI_ПолучениеДанныхТестов.Проверка_S3Успех(Результат);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
Бакет = "opi-dirbucket3";
Результат = OPI_S3.ЗагрузитьОбъект(Наименование, Бакет, Содержимое, ОсновныеДанные);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ЗагрузитьОбъект (DB)", "S3");
OPI_ПолучениеДанныхТестов.Проверка_S3Успех(Результат);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
OPI_S3.УдалитьОбъект(Наименование, Бакет, ОсновныеДанные);
КонецПроцедуры
@ -15232,6 +15243,60 @@
КонецПроцедуры
Процедура S3_ПолучитьОбъект(ПараметрыФункции)
Картинка = ПараметрыФункции["Picture"]; // SKIP
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Картинка); // SKIP
НеобходимыйРазмер = Картинка.Размер(); // SKIP
URL = ПараметрыФункции["S3_URL"];
AccessKey = ПараметрыФункции["S3_AccessKey"];
SecretKey = ПараметрыФункции["S3_SecretKey"];
Region = "BTC";
ОсновныеДанные = OPI_S3.ПолучитьСтруктуруДанных(URL, AccessKey, SecretKey, Region);
Наименование = "picture.jpg";
Бакет = "opi-gpbucket3";
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект", "S3"); // SKIP
OPI_ПолучениеДанныхТестов.Проверка_ДвоичныеДанные(Результат, НеобходимыйРазмер); // SKIP
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]); // SKIP
ВременныйФайл = ПолучитьИмяВременногоФайла();
ОсновныеДанные.Вставить("ChunkSize", 200000);
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные, , , ВременныйФайл);
// END
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект (файл)", "S3");
ФайлРез = Новый Файл(Результат["file"]);
OPI_ПолучениеДанныхТестов.ОжидаетЧто(ФайлРез.Размер() = НеобходимыйРазмер);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
УдалитьФайлы(ВременныйФайл);
Наименование = "bigfile.exe";
Бакет = "newbucket2";
ВременныйФайлБольшой = ПолучитьИмяВременногоФайла();
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные, , , ВременныйФайлБольшой);
ФайлРез = Новый Файл(Результат["file"]);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект (большой, файл)", "S3");
OPI_ПолучениеДанныхТестов.ОжидаетЧто(ФайлРез.Размер() = 34432400);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
УдалитьФайлы(ВременныйФайлБольшой);
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект (большой, ДД)", "S3");
OPI_ПолучениеДанныхТестов.Проверка_ДвоичныеДанные(Результат, 34432400);
КонецПроцедуры
#КонецОбласти
#КонецОбласти

View File

@ -2040,9 +2040,9 @@
КонецФункции
Функция ПолучитьОбщийМодуль(Знач Имя)
Модуль = Вычислить(Имя);
Возврат Модуль;
КонецФункции

View File

@ -1009,7 +1009,7 @@
ДополнитьКомплексныйАтрибут(ОбъектВидео, 21841, 100001, СтруктураURL);
ДополнитьКомплексныйАтрибут(ОбъектВидео, 21837, 100001, СтруктураНаименования);
Если Не OPI_Инструменты.ПолеКоллекцииСуществет(СтруктураТовара, Complex_attributes_) Тогда
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(СтруктураТовара, Complex_attributes_) Тогда
СтруктураТовара.Вставить(Complex_attributes_, Новый Массив);
КонецЕсли;
@ -1044,7 +1044,7 @@
ДополнитьКомплексныйАтрибут(ОбъектОбложка, 21845, 100002, СтруктураURL);
Если Не OPI_Инструменты.ПолеКоллекцииСуществет(СтруктураТовара, Complex_attributes_) Тогда
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(СтруктураТовара, Complex_attributes_) Тогда
СтруктураТовара.Вставить(Complex_attributes_, Новый Массив);
КонецЕсли;
@ -1078,7 +1078,7 @@
СтруктураАтрибута = Новый Структура("id,complex_id,values", IDАтрибута, КомплексныйID, Значения);
Если Не OPI_Инструменты.ПолеКоллекцииСуществет(Коллекция, Attributes_) Тогда
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(Коллекция, Attributes_) Тогда
Коллекция.Вставить(Attributes_, Новый Массив);
КонецЕсли;

View File

@ -1,4 +1,4 @@
// OneScript: ./OInt/core/Modules/OPI_S3.os
// OneScript: ./OInt/core/Modules/OPI_S3.os
// Lib: S3
// CLI: s3
@ -75,15 +75,19 @@
// Отправляет простой http запрос без тела
//
// Параметры:
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// ОжидаютсяДвоичные - Булево - Отключает попытку преобразования ответа в JSON - binary
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - сериализованный JSON ответа от хранилища
Функция ОтправитьЗапросБезТела(Знач Метод, Знач ОсновныеДанные, Знач Заголовки = Неопределено) Экспорт
Функция ОтправитьЗапросБезТела(Знач Метод
, Знач ОсновныеДанные
, Знач ОжидаютсяДвоичные = Ложь
, Знач Заголовки = Неопределено) Экспорт
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, , Заголовки);
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, , ОжидаютсяДвоичные, Заголовки);
Возврат Ответ;
КонецФункции
@ -92,16 +96,21 @@
// Отправляет http запрос с телом
//
// Параметры:
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// Тело - Строка, ДвоичныеДанные - Двоичное тело запроса или путь к файлу - body
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
// Метод - Строка - HTTP метод - method
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса (c полным URL). См. ПолучитьСтруктуруДанных - basic
// Тело - Строка, ДвоичныеДанные - Двоичное тело запроса или путь к файлу - body
// ОжидаютсяДвоичные - Булево - Отключает попытку преобразования ответа в JSON - binary
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - сериализованный JSON ответа от хранилища
Функция ОтправитьЗапросСТелом(Знач Метод, Знач ОсновныеДанные, Знач Тело, Знач Заголовки = Неопределено) Экспорт
Функция ОтправитьЗапросСТелом(Знач Метод
, Знач ОсновныеДанные
, Знач Тело
, Знач ОжидаютсяДвоичные = Ложь
, Знач Заголовки = Неопределено) Экспорт
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, Тело, Заголовки);
Ответ = ОтправитьЗапрос(Метод, ОсновныеДанные, Тело, ОжидаютсяДвоичные, Заголовки);
Возврат Ответ;
КонецФункции
@ -216,7 +225,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, XMLКонфигурация, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, XMLКонфигурация, , Заголовки);
Возврат Ответ;
@ -248,7 +257,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -280,7 +289,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -318,7 +327,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, , Заголовки);
Возврат Ответ;
@ -349,7 +358,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -380,7 +389,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -419,7 +428,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, , Заголовки);
Возврат Ответ;
@ -450,7 +459,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -488,7 +497,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -527,7 +536,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапрос("PUT", ОсновныеДанные, Содержимое, Заголовки);
Ответ = ОтправитьЗапрос("PUT", ОсновныеДанные, Содержимое, , Заголовки);
Возврат Ответ;
@ -570,13 +579,68 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("HEAD", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("HEAD", ОсновныеДанные, , Заголовки);
Ответ["response"] = Новый Структура;
Возврат Ответ;
КонецФункции
// Получить объект
// Получает содержимое объекта из бакета
//
// Примечание:
// Метод в документации AWS: [GetObjectAttributes](@docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html)
// Вы можете использовать поле `ChunkSize` в основных данных для указания минимального размера и величины чанка загрузки по частям
// Например, ChunkSize=X означает, что все файлы, размером больше X (в байтах) будут загружаться по частям, где одна часть будет размером X.
// Загрузка по частям используется для больших файлов. Стандартный размер ChunkSize - 20000000 байт (20 МБайт)
//
// Параметры:
// Наименование - Строка - Наименование объекта в бакете - name
// Бакет - Строка - Имя бакета, в котором находится объект - bucket
// ОсновныеДанные - Структура Из КлючИЗначение - Основные данные запроса. См. ПолучитьСтруктуруДанных - basic
// Каталог - Булево - Истина > Directory Bucket, Ложь > General Purpose Bucket - dir
// Версия - Строка - Токен для получения конкретной версии объекта - ver
// Заголовки - Соответствие Из КлючИЗначение - Дополнительные заголовки запроса, если необходимо - headers
// ПутьСохранения - Строка - Путь для прямой записи файла на диск - out
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - сериализованный JSON ответа от хранилища
Функция ПолучитьОбъект(Знач Наименование
, Знач Бакет
, Знач ОсновныеДанные
, Знач Версия = ""
, Знач Заголовки = Неопределено
, Знач ПутьСохранения = "") Экспорт
Если OPI_Инструменты.ПолеКоллекцииСуществует(ОсновныеДанные, "ChunkSize") Тогда
МаксимальныйРазмер = ОсновныеДанные["ChunkSize"];
Иначе
МаксимальныйРазмер = 20000000;
КонецЕсли;
ИнформацияОбъекта = ПолучитьОписаниеОбъекта(Наименование, Бакет, ОсновныеДанные, Версия);
Если Не OPI_Инструменты.ПолеКоллекцииСуществует(ИнформацияОбъекта, "headers.Content-Length") Тогда
Возврат ИнформацияОбъекта;
КонецЕсли;
РазмерОбъекта = ИнформацияОбъекта["headers"]["Content-Length"];
OPI_ПреобразованиеТипов.ПолучитьЧисло(МаксимальныйРазмер);
OPI_ПреобразованиеТипов.ПолучитьЧисло(РазмерОбъекта);
Если РазмерОбъекта > МаксимальныйРазмер Тогда
Размеры = Новый Структура("object,chunk", РазмерОбъекта, МаксимальныйРазмер);
Ответ = ПолучитьОбъектЧастями(ОсновныеДанные, Заголовки, ПутьСохранения, Размеры);
Иначе
Ответ = ПолучитьОбъектЦеликом(ОсновныеДанные, Заголовки, ПутьСохранения);
КонецЕсли;
Возврат Ответ;
КонецФункции
// Удалить объект
// Удаляет объект из бакета
//
@ -613,7 +677,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -692,7 +756,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, Заголовки);
Ответ = ОтправитьЗапросСТелом("PUT", ОсновныеДанные, ТегиXML, , Заголовки);
Возврат Ответ;
@ -732,7 +796,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -772,7 +836,7 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("DELETE", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -811,7 +875,7 @@
URL = URL + OPI_Инструменты.ПараметрыЗапросаВСтроку(Параметры);
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -850,7 +914,7 @@
URL = URL + OPI_Инструменты.ПараметрыЗапросаВСтроку(Параметры, , Ложь);
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, , Заголовки);
Возврат Ответ;
@ -1140,7 +1204,11 @@
#Область Прочее
Функция ОтправитьЗапрос(Знач Метод, Знач ОсновныеДанные, Знач Тело = Неопределено, Знач Заголовки = Неопределено)
Функция ОтправитьЗапрос(Знач Метод
, Знач ОсновныеДанные
, Знач Тело = Неопределено
, Знач ОжидаютсяДвоичные = Ложь
, Знач Заголовки = Неопределено)
ПроверитьОсновныеДанные(ОсновныеДанные);
@ -1164,7 +1232,7 @@
Запрос.Заголовки.Вставить("Authorization", ЗаголовокАвторизации);
Ответ = OPI_Инструменты.ВыполнитьЗапрос(Запрос, Соединение, Метод, , Истина);
Ответ = ОформитьОтвет(Ответ);
Ответ = ОформитьОтвет(Ответ, ОжидаютсяДвоичные);
Возврат Ответ;
@ -1177,12 +1245,102 @@
ОсновныеДанные.Вставить("URL", URL);
Ответ = ОтправитьЗапросБезТела(Метод, ОсновныеДанные, Заголовки);
Ответ = ОтправитьЗапросБезТела(Метод, ОсновныеДанные, , Заголовки);
Возврат Ответ;
КонецФункции
Функция ПолучитьОбъектЧастями(Знач ОсновныеДанные
, Знач Заголовки
, Знач ПутьСохранения
, Знач Размеры)
ОбщийРазмер = Размеры["object"];
РазмерУчастка = Размеры["chunk"];
ШаблонЗаголовка = "bytes=%1-%2";
НачалоУчастка = 0;
МассивЗаголовков = Новый Массив;
Пока НачалоУчастка < ОбщийРазмер - РазмерУчастка Цикл
КонецУчастка = НачалоУчастка + РазмерУчастка - 1;
НачалоСтр = OPI_Инструменты.ЧислоВСтроку(НачалоУчастка);
КонецСтр = OPI_Инструменты.ЧислоВСтроку(КонецУчастка);
Заголовок = СтрШаблон(ШаблонЗаголовка, НачалоСтр, КонецСтр);
МассивЗаголовков.Добавить(Новый Структура("Заголовок,Позиция", Заголовок, КонецСтр));
НачалоУчастка = КонецУчастка + 1;
КонецЦикла;
Если Не НачалоУчастка = ОбщийРазмер Тогда
Заголовок = "bytes=" + OPI_Инструменты.ЧислоВСтроку(НачалоУчастка) + "-";
СтруктураИнформации = Новый Структура("Заголовок,Позиция", Заголовок, ОбщийРазмер);
МассивЗаголовков.Добавить(СтруктураИнформации);
КонецЕсли;
Если ЗначениеЗаполнено(ПутьСохранения) Тогда
ПотокФайла = Новый ФайловыйПоток(ПутьСохранения, РежимОткрытияФайла.Создать);
Иначе
ПотокФайла = Новый ПотокВПамяти();
КонецЕсли;
ЗаписьФайла = Новый ЗаписьДанных(ПотокФайла);
Для Каждого ТекущийНабор Из МассивЗаголовков Цикл
ЗаголовокУчастка = ТекущийНабор["Заголовок"];
ТекущаяПозиция = ТекущийНабор["Позиция"];
ЗаголовокИсточника = Новый Соответствие();
ЗаголовокИсточника.Вставить("Range", ЗаголовокУчастка);
ДобавитьДополнительныеЗаголовки(Заголовки, ЗаголовокИсточника);
ПромежуточныйРезультат = ПолучитьОбъектЦеликом(ОсновныеДанные, ЗаголовокИсточника);
ЗаписьФайла.Записать(ПромежуточныйРезультат);
// !OInt КБайт = 1024;
// !OInt МБайт = КБайт * КБайт;
// !OInt Сообщить(OPI_Инструменты.ИнформацияОПрогрессе(ТекущаяПозиция, ОбщийРазмер, "МБ", МБайт));
// !OInt ВыполнитьСборкуМусора();
// !OInt ОсвободитьОбъект(ПромежуточныйРезультат);
КонецЦикла;
ЗаписьФайла.Закрыть();
Если ТипЗнч(ПотокФайла) = Тип("ПотокВПамяти") Тогда
Возврат ПотокФайла.ЗакрытьИПолучитьДвоичныеДанные();
Иначе
ПотокФайла.Закрыть();
Возврат Новый Структура("file", ПутьСохранения);
КонецЕсли;
КонецФункции
Функция ПолучитьОбъектЦеликом(Знач ОсновныеДанные
, Знач Заголовки
, Знач ПутьСохранения = "")
Ответ = ОтправитьЗапросБезТела("GET", ОсновныеДанные, Истина, Заголовки);
Если ЗначениеЗаполнено(ПутьСохранения) Тогда
Ответ.Записать(ПутьСохранения);
Возврат Новый Структура("file", ПутьСохранения);
Иначе
Возврат Ответ;
КонецЕсли;
КонецФункции
Функция ОформитьОтвет(Знач Ответ, Знач ОжидаютсяДвоичные = Ложь)
Статус = Ответ.КодСостояния;

View File

@ -1,4 +1,4 @@
// OneScript: ./OInt/tests/Modules/internal/OPI_Тесты.os
// OneScript: ./OInt/tests/Modules/internal/OPI_Тесты.os
// MIT License
@ -2155,6 +2155,7 @@
S3_УдалитьТегиОбъекта(ПараметрыТеста);
S3_ПолучитьСписокОбъектов(ПараметрыТеста);
S3_ПолучитьСписокВерсийОбъектов(ПараметрыТеста);
S3_ПолучитьОбъект(ПараметрыТеста);
S3_УдалитьОбъект(ПараметрыТеста);
S3_УдалитьБакет(ПараметрыТеста);
@ -15053,6 +15054,16 @@
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ЗагрузитьОбъект", "S3");
OPI_ПолучениеДанныхТестов.Проверка_S3Успех(Результат);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
Бакет = "opi-dirbucket3";
Результат = OPI_S3.ЗагрузитьОбъект(Наименование, Бакет, Содержимое, ОсновныеДанные);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ЗагрузитьОбъект (DB)", "S3");
OPI_ПолучениеДанныхТестов.Проверка_S3Успех(Результат);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
OPI_S3.УдалитьОбъект(Наименование, Бакет, ОсновныеДанные);
КонецПроцедуры
@ -15232,6 +15243,60 @@
КонецПроцедуры
Процедура S3_ПолучитьОбъект(ПараметрыФункции)
Картинка = ПараметрыФункции["Picture"]; // SKIP
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(Картинка); // SKIP
НеобходимыйРазмер = Картинка.Размер(); // SKIP
URL = ПараметрыФункции["S3_URL"];
AccessKey = ПараметрыФункции["S3_AccessKey"];
SecretKey = ПараметрыФункции["S3_SecretKey"];
Region = "BTC";
ОсновныеДанные = OPI_S3.ПолучитьСтруктуруДанных(URL, AccessKey, SecretKey, Region);
Наименование = "picture.jpg";
Бакет = "opi-gpbucket3";
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект", "S3"); // SKIP
OPI_ПолучениеДанныхТестов.Проверка_ДвоичныеДанные(Результат, НеобходимыйРазмер); // SKIP
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]); // SKIP
ВременныйФайл = ПолучитьИмяВременногоФайла();
ОсновныеДанные.Вставить("ChunkSize", 200000);
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные, , , ВременныйФайл);
// END
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект (файл)", "S3");
ФайлРез = Новый Файл(Результат["file"]);
OPI_ПолучениеДанныхТестов.ОжидаетЧто(ФайлРез.Размер() = НеобходимыйРазмер);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
УдалитьФайлы(ВременныйФайл);
Наименование = "bigfile.exe";
Бакет = "newbucket2";
ВременныйФайлБольшой = ПолучитьИмяВременногоФайла();
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные, , , ВременныйФайлБольшой);
ФайлРез = Новый Файл(Результат["file"]);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект (большой, файл)", "S3");
OPI_ПолучениеДанныхТестов.ОжидаетЧто(ФайлРез.Размер() = 34432400);
ОсновныеДанные.Вставить("URL", ПараметрыФункции["S3_URL"]);
УдалитьФайлы(ВременныйФайлБольшой);
Результат = OPI_S3.ПолучитьОбъект(Наименование, Бакет, ОсновныеДанные);
OPI_ПолучениеДанныхТестов.ЗаписатьЛог(Результат, "ПолучитьОбъект (большой, ДД)", "S3");
OPI_ПолучениеДанныхТестов.Проверка_ДвоичныеДанные(Результат, 34432400);
КонецПроцедуры
#КонецОбласти
#КонецОбласти