Работа с двоичными данными на примере чтения файлов изображений. Новые возможности 8.3.9

27.06.18

Разработка - Универсальные функции

В статье приводятся новые функции по работе с двоичными данными, появившимися в версии платформы 8.3.9 , на примере анализа формата и размера изображений. А также пример отправки изображения через API ВКонтакте с помощью новых объектов (без использования ОбъединитьФайлы())

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Работа с двоичными данными на примере чтения файлов изображений
.epf 8,25Kb
26
26 Скачать (1 SM) Купить за 1 850 руб.

В данной статье рассмотрим применение новых методов платформы 8.3.9  по работе с двоичными данными. Сначала немного теории.

Как было раньше

Ранее 1С предоставляла для работы с двоичными данными одноименный тип и некоторые методы работы с файлами. Думаю, многим известна ситуация, когда для отправки файла в формате multipart/form-data использовался метод «ОбъединитьФайлы». Это было связано  с отсутствием методов по работе  с внутренним содержимым двоичных данных.

Что нового

В версии 8.3.9 эта ситуация меняется. Разработчики платформы предоставили в наше распоряжение несколько новых типов и методов. Основным из них является обобщенный объект Поток. Потоков бывает три типа: Поток, ФайловыйПоток и ПотокВПамяти.

Основным преимуществом потоков является их способность работать с данными произвольного объема. Но в то же время они предоставляют ограниченные возможности. 

Для получения расширенных возможностей необходимо на основе потока создать объект ЧтениеДанных. С помощью него можно читать отдельные байты, символы, числа.

Побайтовые операции

Для анализа работы новых методов возьмем  за пример анализ параметров изображений для загрузки через API ВКонтакте.
Требования для загрузки фото в товар группы:

Допустимые форматы: JPG, PNG, GIF. 
Ограничения: минимальный размер фото — 200x200px, сумма высоты и ширины не более 14000px, файл объемом не более 50 МБ.
 
Нас будет интересовать формат файла, его разрешение и вес.

Внимание! Формат файла JPG не обрабатывается в обработке.

Вес файла

Сначала на основе выбранного файла создадим ФайловыйПоток и сможем сразу получить Размер файла в байтах:

ПотокИсходный = ФайловыеПотоки.ОткрытьДляЧтения(Объект.ИмяФайла);

///1. Размер файла
Объект.РазмерФайла = Строка(ПотокИсходный.Размер() / 1024) + " Кб";

Формат файла

 Каждый формат файла отличается друг от друга внутренней структурой. Отличительными особенностями каждого формата будем называть сигнатурами.
Зададим сигнатуры для каждого формата:

///Зададим сигнатуры нужных форматов 
МассивДопустимыхФорматов = Новый Соответствие;
МассивДопустимыхФорматов.Вставить("PNG", 	РазложитьСтрокуВМассивПодстрок("137,80,78,71,13,10,26,10"));
//МассивДопустимыхФорматов.Вставить("JPEG", 	РазложитьСтрокуВМассивПодстрок("255,216"));
МассивДопустимыхФорматов.Вставить("GIF1", 	РазложитьСтрокуВМассивПодстрок("71,73,70,56,57,97"));
МассивДопустимыхФорматов.Вставить("BMP", 	РазложитьСтрокуВМассивПодстрок("66,77"));
МассивДопустимыхФорматов.Вставить("TIF", 	РазложитьСтрокуВМассивПодстрок("73,73,42,0"));
МассивДопустимыхФорматов.Вставить("TIF1", 	РазложитьСтрокуВМассивПодстрок("77,77,0,42"));

Каким образом получены сигнатуры?

В hex-записи сигнатура PNG файла выглядит так и состоит из 8 байт:
89 50 4E 47 0D 0A 1A 0A

Это можно проверить, открыв любой PNG файл в HEX-редакторе. Например, возьмем иконку месседжера Telegram размером 32*32.

Переведем эти значения из 16-ной системы в 10-ную и получим значения:
"137,80,78,71,13,10,26,10"

Аналогично поступаем с остальными форматами.

Проверяем формат файла

Создаем объект ЧтениеДанных на основе потока:
ЧтениеДанных = Новый ЧтениеДанных(ПотокИсходный, КодировкаТекста.ANSI, ПорядокБайтов.BigEndian);
ИскомыйТипФайла = ПроверитьСоответствиеТипаФайла(ЧтениеДанных, МассивДопустимыхФорматов);
	
	Если ИскомыйТипФайла = Неопределено Тогда
		Сообщить("Не поддерживаемый тип файла");
		Возврат;
	КонецЕсли;

&НаСервере
Функция ПроверитьСоответствиеТипаФайла(ЧтениеДанных, МассивДопустимыхФорматов)
	
	МаксРазмер = 0;
	Для Каждого ТипФайла Из МассивДопустимыхФорматов Цикл
		МаксРазмер = Макс(МаксРазмер, ТипФайла.Значение.Количество());		
	КонецЦикла; 
	
	//прочитаем в новый буфер максимальный размер сигнатуры
	БуферПроверка = ЧтениеДанных.ПрочитатьВБуферДвоичныхДанных(МаксРазмер);
	
	Для Каждого ТипФайла Из МассивДопустимыхФорматов Цикл
		//Хотелось бы конечно напрямую сравнить Буфер = Буфер, но так не работает
		//Приходится сравнивать каждый байт
		
		ДвоичнаяСигнатураФормата = ПолучитьСигнатуруФорматаВДвоичномВиде(ТипФайла.Значение);
		БуферПроверкаТипаФайла = БуферПроверка.Прочитать(0, ДвоичнаяСигнатураФормата.Размер);
		
		Сч = 0;
		Равны = Истина;
		Для Каждого Счет Из БуферПроверкаТипаФайла Цикл
			Если Счет <> ДвоичнаяСигнатураФормата[Сч] Тогда
				Равны = Ложь;	
				Прервать;
			КонецЕсли; 
		    Сч = Сч + 1; 
		КонецЦикла; 
		Если Равны Тогда
			Возврат ТипФайла.Ключ;
		КонецЕсли; 
	КонецЦикла; 

	Возврат НЕопределено;

КонецФункции

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

Ищем размеры изображения

В каждом формате файла размеры изображения хранятся в разных местах. Например, в PNG они находятся после маркера «IHDR». Зная это, с помощью новых методов, мы можем переместить указатель на нужную позицию :
ЧтениеДанных.ПропуститьДо("IHDR", КодировкаТекста.ANSI);

Так же из спецификации формата PNG известно, что ширина и высота изображения занимают по 4 байта. Сначала прочитаем все 8 байтов, а потом отдельно ширину и высоту:

БуферЗаголовок 				= ЧтениеДанных.ПрочитатьВБуферДвоичныхДанных(8);
ОБъект.ШиринаИзображения 	= БуферЗаголовок.Прочитать(0, 4).ПрочитатьЦелое32(0, ПорядокБайтов.BigEndian);
Объект.ВысотаИзображения 	= БуферЗаголовок.Прочитать(4, 4).ПрочитатьЦелое32(0, ПорядокБайтов.BigEndian);

В методе ПрочитатьВБуферДвоичныхДанных() мы указываем общее количество байтов, которое хотим прочитать в буфер двоичных данных. Далее этот буфер читаем методом Прочитать(0, 4) – где 0 это позиция, а 4 – количество байтов для чтения.

С GIF ситуация немного другая. Т.к. мы использовали  исходный объект ЧтениеДанных для проверки соответствия форматам изображений, то указатель в этом экземпляре объекта переместился на какое-то количество позиций. 

Если мы продолжим читать этот экземпляр, то не сможем найти размеры изображения. Поэтому используем метод потока Перейти() и создаем новое ЧтениеДанных на основе нашего исходного потока. Дальше как обычно читаем размеры.

Как выглядит внутренняя структура файла GIF:
W и H - это ширина и высота. Чтобы до них добраться читаем в буфер 10 байт и получаем ширину и высоту с позиций 6 и 8, прочитав в каждом случае по 2 байта.
 
ТекПозиция 		= ПотокИсходный.ТекущаяПозиция();
ПотокИсходный.Перейти(-ТекПозиция, ПозицияВПотоке.Текущая);
ЧтениеДанныхGIF = Новый ЧтениеДанных(ПотокИсходный, КодировкаТекста.ANSI, ПорядокБайтов.LittleEndian);
БуферРазмеры 				= ЧтениеДанныхGIF.ПрочитатьВБуферДвоичныхДанных(10);
Объект.ШиринаИзображения 	= БуферРазмеры.Прочитать(6,2).ПрочитатьЦелое16(0, ПорядокБайтов.LittleEndian);
Объект.ВысотаИзображения 	= БуферРазмеры.Прочитать(8,2).ПрочитатьЦелое16(0, ПорядокБайтов.LittleEndian);

 Таким образом, зная спецификации нужных форматов, мы проверяем формат файла, размеры изображения.

Отправка изображения через API ВКонтакте

Привожу пример работающего кода  отправки сообщения multipart/form-data (проверено на отправке фото товара в группу ВКонтакте) (в обработке не приведен этот код):

//здесь получается объект с типом Картинка
Изображение = Номенклатура.ОсновноеИзображение.Хранилище.Получить();
//в функцию передаем двоичные данные картинки стандартным методом ПолучитьДвоичныеДанные()
ДвоичныеДанныеТело = СобратьИзображениеИзДвоичныхДанных(Изображение.ПолучитьДвоичныеДанные(), Boundary, ИмяФайлаДляЗагрузки);


Функция СобратьИзображениеИзДвоичныхДанных(ДвоичныеДанныеИзображения, Boundary, ИмяФайлаДляЗагрузки)
	
	ПотокТело = Новый ПотокВПамяти();
	ЗаписьДанных = Новый ЗаписьДанных(ПотокТело);
	
	ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
	ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайлаДляЗагрузки + """");	
	
	ЗаписьДанных.ЗаписатьСтроку("Content-Type: image/jpeg");
	ЗаписьДанных.ЗаписатьСтроку("");

	ЗаписьДанных.Записать(ДвоичныеДанныеИзображения);		
	//Завершение раздела двоичных данных
	ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
	
	//Завершение сообщения для сервера
	ЗаписьДанных.ЗаписатьСтроку("--" + Boundary + "--");	
	
	ЗаписьДанных.Закрыть();
	
	ДвоичныеДанныеТело = ПотокТело.ЗакрытьИПолучитьДвоичныеДанные();
	
	Возврат ДвоичныеДанныеТело;
	
	
КонецФункции      

 

двоичные данные формат файла размер файла API Вконтакте

См. также

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Эта небольшая статья - некоторого рода шпаргалка по файловым потокам: как и зачем с ними работать, какие преимущества это дает.

23.06.2024    7443    bayselonarrend    20    

154

Универсальные функции Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Благодаря этим пяти строчкам можно больше не заморачиваться с загрузкой из внешних файлов. Пользуюсь везде, всегда и постоянно.

21.05.2024    20123    dimanich70    81    

144

Перенос данных 1C Механизмы платформы 1С Системный администратор Программист Стажер Платформа 1С v8.3 Бесплатно (free)

Вы все еще регистрируете изменения только на Планах обмена и Регистрах сведений?

11.12.2023    11222    dsdred    44    

130

Механизмы платформы 1С Программист Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    23757    SeiOkami    48    

135

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    14730    YA_418728146    7    

166

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    3580    56    progmaster    8    

4

Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Рассмотрим новую возможность 8.3.24 и как её можно эффективно использовать

27.06.2023    25523    SeiOkami    31    

113
Вознаграждение за ответ
Показать полностью
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. CyberCerber 872 14.11.16 09:27 Сейчас в теме
Добрый день

Спасибо за информацию, записывать файл в multipart/form-data стало теперь намного красивее.

Вот только:

Объект.РазмерФайла = Строка(ПотокИсходный.Размер() / 1000) + " Кб";

Почему делите на 1000, а не 1024?
maksa2005; корум; frkbvfnjh; aximo; +4 Ответить
2. Anton64 192 14.11.16 10:33 Сейчас в теме
(1) CyberCerber, Спасибо, ошибка закралась. Исправил
20. kild 92 25.06.18 17:52 Сейчас в теме
(2) Почему не исправили в прикрепленной обработке?
3. Armando 1401 14.11.16 11:14 Сейчас в теме
Подсчет количества страниц TIFF
&НаКлиенте
Процедура Команда1(Команда)
	
	ПапкаСФайлами = "";
	МассивФайлов = НайтиФайлы(ПапкаСФайлами, "*.tif*");
	Для Каждого Файл Из МассивФайлов Цикл
		ФайловыйПоток = ФайловыеПотоки.ОткрытьДляЧтения(Файл.ПолноеИмя);
		Буфер = Новый БуферДвоичныхДанных( 4 );
		ФайловыйПоток.Прочитать(Буфер, 0, Буфер.Размер);
		Если Буфер[0] = 73 И Буфер[1] = 73 И Буфер[2] = 42 И Буфер[3] = 0 Тогда
			ПорядокБ = ПорядокБайтов.LittleEndian;
		ИначеЕсли Буфер[0] = 77 И Буфер[1] = 77 И Буфер[2] = 0 И Буфер[3] = 42 Тогда
			ПорядокБ = ПорядокБайтов.BigEndian;
		Иначе
			Сообщить(Файл.ПолноеИмя + ": формат не поддерживается");
			Продолжить;
		КонецЕсли;
		ФайловыйПоток.Перейти(4, ПозицияВПотоке.Начало);
		Буфер = Новый БуферДвоичныхДанных( 8 );
		ФайловыйПоток.Прочитать(Буфер, 0, Буфер.Размер);
		СмещениеПервогоIFD = Буфер.ПрочитатьЦелое32(0, ПорядокБ);
		
		Количество = Команда1Фрагмент(СмещениеПервогоIFD, ФайловыйПоток, 0, ПорядокБ);
		Сообщить(Файл.ПолноеИмя + ": " + Количество);
		
		ФайловыйПоток.Закрыть();
	КонецЦикла;
	
КонецПроцедуры

&НаКлиенте
Функция Команда1Фрагмент(СмещениеСледущего, Знач ФайловыйПоток, Счетчик, ПорядокБ)
	
	Перем Буфер, КоличествоТэгов, СмещениеСледущегоСмещения;
	
	Пока СмещениеСледущего > 0 Цикл
		Если Счетчик = 999 Тогда
			Прервать; // что-то не так
		КонецЕсли;
		Счетчик = Счетчик + 1;
		ФайловыйПоток.Перейти(СмещениеСледущего, ПозицияВПотоке.Начало);
		Буфер = Новый БуферДвоичныхДанных( 8 );
		ФайловыйПоток.Прочитать(Буфер, 0, Буфер.Размер);
		КоличествоТэгов = Буфер.ПрочитатьЦелое16(0, ПорядокБ);
		СмещениеСледущегоСмещения = СмещениеСледущего + 2 + (КоличествоТэгов * 12);
		ФайловыйПоток.Перейти(СмещениеСледущегоСмещения, ПозицияВПотоке.Начало);
		Буфер = Новый БуферДвоичныхДанных( 8 );
		ФайловыйПоток.Прочитать(Буфер, 0, Буфер.Размер);
		СмещениеСледущего = Буфер.ПрочитатьЦелое32(0, ПорядокБ);
		Команда1Фрагмент(СмещениеСледущего, ФайловыйПоток, Счетчик, ПорядокБ);
	КонецЦикла;
	
	Возврат Счетчик;
	
КонецФункции
Показать
kraynev-navi; swiss-garant; VasilVtoroy; Anton64; DrAku1a; +5 Ответить
4. kredko 20 15.11.16 04:32 Сейчас в теме
Переведем эти значения из 16-ной системы в двоичную и получим значения:
"137,80,78,71,13,10,26,10"

Может всё же в десятиричную систему счисления?
5. Anton64 192 15.11.16 07:23 Сейчас в теме
(4) kredko, да, всё верно, спасибо
6. Serj1C 483 16.11.16 08:06 Сейчас в теме
Как же мне не хватало работы с двоичными данными в 2010 году...
Но BMP читать и писать уже тогда получалось средствами платформы) http://infostart.ru/public/77713/
7. sml 41 17.11.16 09:44 Сейчас в теме
Плюсанул за подробное описание форматов файлов
8. mixperm 68 18.11.16 19:11 Сейчас в теме
Отправка картинки в ВК не удалась. Буду очень благодарен если сообщите кусок кода где на upload_url отправляется эта картинка
9. Anton64 192 19.11.16 17:57 Сейчас в теме
(8) mixperm,
Код
Поручик; chizbo; +2 Ответить
10. Yashazz 4790 21.11.16 17:10 Сейчас в теме
Ай-ай, такие прогрессивные вещи описываете, а всё РазложитьСтрокуВМассивПодстрок вместо СтрРазделить пользуете)))

Если честно, не особенно понимаю прикладную пользу этих новшеств. Где это реально сильно и позарез надо?
TreeDogNight; Сурикат; user636219_dmitriy.gomzin; +3 Ответить
12. DrAku1a 1745 09.01.17 08:28 Сейчас в теме
(10) Разбор кода POST-запроса для HTTP или WEB сервисов.
11. Godman 70 23.11.16 16:29 Сейчас в теме
Ну и зачем это надо? Теперь вирусы писать станет проще - прямо средствами нативного языка. Как щас вижу баннер от Касперского: "Надежная защита от 1С. Доступно и всерьёз!"
Поручик; DrAku1a; TreeDogNight; da.buraev; bow; brr; +6 Ответить
13. fokin 10.01.18 10:40 Сейчас в теме
бьюсь в поисках
а есть способ определить цветное это изображение или оттенки-серого или ч/б ?
14. DenisCh 10.01.18 10:49 Сейчас в теме
Я себе через бинарные файлы вот эту фигню http://ftsc.org/docs/fsc-0048.002 читаю )))
15. fokin 10.01.18 11:22 Сейчас в теме
(14) а можно поподробнее? что это?
18. fokin 10.01.18 13:40 Сейчас в теме
(16) чем фидо мне поможет?
19. DenisCh 10.01.18 15:10 Сейчас в теме
(18) Тебе ничем. я просто привёл пример того, что я делаю с бинарными файлами )))
17. fokin 10.01.18 13:35 Сейчас в теме
21. kild 92 25.06.18 19:34 Сейчас в теме +2 $m
Скачал обработку. JPEG файлы неправильно определяет ширину высоту. Зря потраченные стартмани.
Например, прикрепил картинку 800x600. Твоя обработка показывает 55653х62772. Неужели за 2 года никто не проверил?
Прикрепленные файлы:
22. Anton64 192 27.06.18 09:49 Сейчас в теме +30 $m
(21) Убрал описание JPEG файлов из публикации. Вернул ваши кровно заработанные зря потраченные стартмани в двойном размере.
Sergafan10; Danil.Potapov; juker; pm74; GreenDragon; +5 Ответить
23. pavel_pozdeev 315 16.04.21 11:52 Сейчас в теме
(22) а тип файл jpeg она верно определяет? У меня задача определить тип (jpeg, png, tiff, ...) по Картинке
25. kembrik 10 27.01.22 17:52 Сейчас в теме
(22) Честно говоря, не понял почему вы с шириной-высотой JPEG не довели до конца. Там же сначала ищем первый байт маркера (255) потом смотрим что идёт за ним (нам нужен 192,193 или 194)

Размер секции читаем как .ПрочитатьЦелое16() - 2

И дальше дело техники, сначала точность данных как .ПрочитатьБайт() и дважды .ПрочитатьЦелое16() - высота и ширина
24. zna.miass 30.08.21 05:58 Сейчас в теме
А как используя двоичные данные вырезать область картинки по координатам?
Оставьте свое сообщение