В процессе разработки в среде «1С:Предприятие 8.3» программисты часто сталкиваются с неочевидным поведением платформы при работе с внешними файлами. Один из наиболее интересных и коварных нюансов связан с созданием объекта Картинка из файлов, которые технически представляют собой ZIP-архивы (включая популярный формат табличных документов Excel — .xlsx). При попытке программно инициализировать картинку из такого файла и затем проверить ее формат с помощью метода Формат(), платформа старых релизов выбрасывает необрабатываемое исключение (или аварийно завершает работу сеанса) вместо того, чтобы просто вернуть ожидаемое значение ФорматКартинки.НеизвестныйФормат. В этой статье мы подробно проанализируем эту ситуацию, выясним внутренние причины такого поведения платформы и разберем надежные методы обхода данной проблемы.
Рассмотрим типичный сценарий: разработчик реализует механизм загрузки изображений, которые пользователи могут выбирать произвольно. Для проверки того, является ли загруженный файл корректным графическим файлом, традиционно используется следующий код:
Результат = Новый Картинка("c:\test.xlsx", Ложь);
Если Результат.Формат() = ФорматКартинки.НеизвестныйФормат Тогда
Сообщить("НеизвестныйФормат");
Иначе
Сообщить("Ok");
КонецЕсли;
Казалось бы, логика абсолютно прозрачна. Если пользователь подсунет файл Excel (.xlsx) или обычный ZIP-архив, конструктор должен либо выдать ошибку сразу при создании, либо вернуть объект, метод Формат() которого вернет ФорматКартинки.НеизвестныйФормат. Именно так это и работало на старой платформе «1С:Предприятие 8.2».
Однако на платформе 8.3 (особенно в релизах до 8.3.10) этот код вызывает падение системы в аварийное исключение при попытке вызвать метод Формат(). Почему же «штатный» файл Excel ломает логику работы с картинками?
Проанализируем ситуацию с технической стороны. Файлы формата .xlsx (а также .docx, .pptx и другие форматы Office OpenXML) физически представляют собой обычные сжатые ZIP-архивы. Платформа 1С определяет формат файла не по его расширению (которое пользователь может легко переименовать), а по его внутренней сигнатуре — первым байтам заголовка (так называемому магическому числу). Для ZIP-архивов этой сигнатурой являются символы PK.
Начиная с версии платформы 8.3.3, разработчики «1С» внедрили механизм поддержки так называемых наборов вариантов картинок (или мультивариантных картинок). Это было сделано для поддержки масштабирования интерфейса «Такси» на экранах с высокой плотностью пикселей (Retina, UltraHD, 4K) и на мобильных устройствах. Чтобы иконки и элементы управления выглядели четко при любом масштабе экрана (от 85% до 400%), платформа должна уметь динамически выбирать изображение нужного разрешения.
Технически такой набор вариантов картинок упаковывается разработчиком в обычный ZIP-архив, внутри которого должны находиться:
manifest.xml, описывающий соответствие файлов масштабам экрана.Рассмотрим пример содержимого правильного файла manifest.xml для набора картинок:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Picture>
<PictureVariant name="85.png" screenDensity="bldpi" glyphWidth="14" glyphHeight="14"/>
<PictureVariant name="100.png" screenDensity="ldpi" glyphWidth="16" glyphHeight="16"/>
<PictureVariant name="125.png" screenDensity="aldpi" glyphWidth="20" glyphHeight="20"/>
<PictureVariant name="150.png" screenDensity="mdpi" glyphWidth="24" glyphHeight="24"/>
<PictureVariant name="200.png" screenDensity="hdpi" glyphWidth="32" glyphHeight="32"/>
<PictureVariant name="300.png" screenDensity="xdpi" glyphWidth="48" glyphHeight="48"/>
<PictureVariant name="400.png" screenDensity="udpi" glyphWidth="64" glyphHeight="64"/>
</Picture>
Теперь соберем цепочку событий воедино:
.zip, и .xlsx).Новый Картинка() считает, что ему подсунули официальный контейнер мультивариантной картинки (набор вариантов). Объект успешно создается в памяти.НаборВариантов принимает значение Истина.Формат(), платформа пытается проанализировать содержимое архива, найти там файл manifest.xml и прочитать его структуру.Поскольку мы не можем гарантировать, что пользователи никогда не попытаются загрузить архив или файл Excel вместо картинки, мы должны защитить наше прикладное решение от сбоев. Рассмотрим три основных подхода к решению этой проблемы.
Самый надежный и простой способ предотвратить падение — не передавать файлы сомнительных форматов в конструктор Картинка. Мы можем предварительно проверить расширение файла по белому списку разрешенных графических форматов.
Разберем по шагам пример реализации такой функции:
Функция ФайлЯвляетсяКартинкой(ПутьКФайлу) Экспорт
ФайлПроверки = Новый Файл(ПутьКФайлу);
Если Не ФайлПроверки.Существует() Тогда
Возврат Ложь;
КонецЕсли;
Расширение = НРег(ФайлПроверки.Расширение);
// Формируем белый список допустимых графических расширений
ДопустимыеРасширения = Новый Соответствие;
ДопустимыеРасширения.Вставить(".png", Истина);
ДопустимыеРасширения.Вставить(".jpg", Истина);
ДопустимыеРасширения.Вставить(".jpeg", Истина);
ДопустимыеРасширения.Вставить(".bmp", Истина);
ДопустимыеРасширения.Вставить(".gif", Истина);
ДопустимыеРасширения.Вставить(".ico", Истина);
ДопустимыеРасширения.Вставить(".svg", Истина);
ДопустимыеРасширения.Вставить(".tif", Истина);
ДопустимыеРасширения.Вставить(".tiff", Истина);
Если ДопустимыеРасширения[Расширение] = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
Возврат Истина;
КонецФункции
Этот метод полностью исключает передачу архивов .zip и документов .xlsx в объект Картинка, страхуя систему от непредвиденных падений.
Если специфика вашей конфигурации требует обработки файлов без жесткой привязки к расширениям (например, картинки могут храниться в файлах без расширений во временных каталогах), необходимо обязательно оборачивать работу со свойствами созданного объекта в блок Попытка...Исключение.
Посмотрим на пример безопасной инициализации:
Функция ПолучитьБезопаснуюКартинку(ПутьКФайлу)
Попытка
ТестоваяКартинка = Новый Картинка(ПутьКФайлу, Ложь);
// Попытка вызвать метод, потенциально приводящий к сбою
ФорматИзображения = ТестоваяКартинка.Формат();
Если ФорматИзображения = ФорматКартинки.НеизвестныйФормат Тогда
Возврат Неопределено;
КонецЕсли;
Возврат ТестоваяКартинка;
Исключение
// Сюда мы попадем, если был передан поврежденный ZIP/XLSX
ЗаписьЖурналаРегистрации("Загрузка картинок",
УровеньЖурналаРегистрации.Предупреждение,
,
,
"Ошибка при анализе формата файла: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
Использование такой конструкции гарантирует, что даже при возникновении внутренней ошибки платформы при парсинге псевдо-манифеста, ваше прикладное решение продолжит работу, зафиксировав проблему в журнале регистрации.
Если вы хотите детально обрабатывать ситуации, когда пользователь действительно загружает ZIP-архив, содержащий набор картинок (например, при кастомизации интерфейса), вы можете анализировать специализированное свойство картинки НаборВариантов.
Проанализируем следующий алгоритм:
Попытка
РезультатКартинка = Новый Картинка(ПутьКФайлу, Ложь);
Если РезультатКартинка.НаборВариантов Тогда
// Платформа определила файл как ZIP-контейнер картинок.
// Проверим, не пустой ли он, попытавшись преобразовать его
// или обратившись к его базовым свойствам в безопасном блоке.
Попытка
ШиринаКартинки = РезультатКартинка.Ширина();
Если ШиринаКартинки = 0 Тогда
// Картинка пустая (манифест не найден или некорректен)
РезультатКартинка = Неопределено;
КонецЕсли;
Исключение
РезультатКартинка = Неопределено;
КонецПопытки;
ИначеЕсли РезультатКартинка.Формат() = ФорматКартинки.НеизвестныйФормат Тогда
РезультатКартинка = Неопределено;
КонецЕсли;
Исключение
РезультатКартинка = Неопределено;
КонецПопытки;
Данный подход позволяет вашему приложению корректно работать как с обычными одиночными картинками, так и с профессиональными масштабируемыми ZIP-наборами вариантов картинок, отсекая при этом "битые" архивы и сторонние файлы вроде документов Excel.
Поведение платформы 1С 8.3 при попытке прочитать свойства картинок из файлов .xlsx и .zip обусловлено официальной поддержкой масштабируемых наборов графики. Платформа распознает их по сигнатуре архива, но ломается при отсутствии обязательного служебного файла manifest.xml внутри. Чтобы обезопасить свои конфигурации от неожиданных падений, всегда сочетайте проверку расширений файлов по белому списку с оборачиванием операций разбора свойств картинок в блоки Попытка...Исключение.