При разработке механизмов загрузки номенклатуры, контрагентов или файлов часто возникает задача программно сохранить графический файл с диска в реквизит объекта информационной базы, имеющий тип ХранилищеЗначения. Нередко разработчики сталкиваются с ситуацией, когда программный код выполняется без видимых ошибок и исключений, однако само изображение в реквизит не записывается либо возникают ошибки доступа к файлам на диске.
В этой статье мы подробно разберем, почему возникают подобные проблемы, проанализируем частые ошибки при работе с типами Картинка и ДвоичныеДанные, а также рассмотрим рабочие примеры кода для сохранения и последующего отображения данных.
Проанализируем типичный ошибочный код, с которым часто сталкиваются начинающие разработчики:
// Ошибочный подход
Файл = Новый Картинка(ВыборкаЭлементы.Изображение);
УстановкаИзображенияНоменклатуры.ОсновноеИзображение = Новый ХранилищеЗначения(Файл, Новый СжатиеДанных);
УстановкаИзображенияНоменклатуры.Записать();
УдалитьФайлы(ВыборкаЭлементы.Изображение);
Выясним причину, почему такой алгоритм не работает или приводит к скрытым сбоям:
Новый СжатиеДанных является синтаксически неверной. В платформе 1С СжатиеДанных — это системное перечисление, а не объектный тип. Для него нельзя вызывать оператор Новый. Правильно передавать значение перечисления напрямую (например, СжатиеДанных.Максимальное) либо вообще опускать второй параметр конструктора ХранилищеЗначения, так как по умолчанию платформа автоматически применяет оптимальное сжатие.Новый Картинка(ПутьКФайлу) платформа удерживает файловый дескриптор занятым до тех пор, пока сборщик мусора не освободит объект. Если сразу после этого попытаться выполнить метод УдалитьФайлы(), операционная система может вернуть ошибку «Файл занят другим процессом».ХранилищеЗначения рекомендуется помещать именно чистые ДвоичныеДанные, а не прикладной объект Картинка. Это обеспечивает универсальность хранения, уменьшает накладные расходы и гарантирует корректную десериализацию на любой стороне клиент-серверного взаимодействия.НайтиПоКоду() в случае отсутствия элемента возвращает пустую ссылку. Вызов метода ПолучитьОбъект() от пустой ссылки возвращает значение Неопределено, что приведет к ошибке при обращении к реквизитам. Перед записью обязательно требуется проверка ЗначениеЗаполнено().Рассмотрим наиболее компактный, производительный и надежный способ сохранения файла в реквизит. Мы сразу инициализируем объект ДвоичныеДанные, передавая путь к файлу. При таком подходе файл быстро считывается в память, дескриптор освобождается, и исходный временный файл можно сразу удалять без риска блокировки.
СсылкаНоменклатура = Справочники.Номенклатура.НайтиПоКоду(ВыборкаЭлементы.Код);
Если ЗначениеЗаполнено(СсылкаНоменклатура) Тогда
ОбъектНоменклатура = СсылкаНоменклатура.ПолучитьОбъект();
// Считываем файл напрямую в двоичные данные
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ВыборкаЭлементы.ИмяФайлаИзображения);
// Помещаем в ХранилищеЗначения со стандартным сжатием
ОбъектНоменклатура.ОсновноеИзображение = Новый ХранилищеЗначения(ДвоичныеДанныеФайла);
ОбъектНоменклатура.Записать();
// Удаляем временный файл с диска
УдалитьФайлы(ВыборкаЭлементы.ИмяФайлаИзображения);
КонецЕсли;
Разберем вариант, когда перед сохранением требуется предварительно проверить валидность самого графического файла или получить его формат с помощью объекта Картинка.
ИмяФайла = ВыборкаЭлементы.ИмяФайлаИзображения;
КартинкаОбъект = Новый Картинка(ИмяФайла);
// Извлекаем двоичные данные из созданного объекта
ДвоичныеДанныеКартинки = КартинкаОбъект.ПолучитьДвоичныеДанные();
СсылкаНоменклатура = Справочники.Номенклатура.НайтиПоКоду(ВыборкаЭлементы.Код);
Если ЗначениеЗаполнено(СсылкаНоменклатура) Тогда
ОбъектНоменклатура = СсылкаНоменклатура.ПолучитьОбъект();
ОбъектНоменклатура.ОсновноеИзображение = Новый ХранилищеЗначения(ДвоичныеДанныеКартинки);
ОбъектНоменклатура.Записать();
КонецЕсли;
Рассмотрим, как извлечь двоичные данные из реквизита типа ХранилищеЗначения и отобразить их пользователю.
Для обычных форм:
ДвоичныеДанные = Объект.ОсновноеИзображение.Получить();
Если ДвоичныеДанные <> Неопределено Тогда
ЭлементыФормы.ПолеКартинки.Картинка = Новый Картинка(ДвоичныеДанные);
КонецЕсли;
Для управляемых форм:
В управляемом интерфейсе поле картинки связывается со строковым реквизитом формы, содержащим навигационную ссылку на временное хранилище. На сервере мы извлекаем данные и помещаем их во временное хранилище:
&НаСервере
Процедура ПрочитатьИзображениеНаСервере()
ДвоичныеДанные = Объект.ОсновноеИзображение.Получить();
Если ДвоичныеДанные <> Неопределено Тогда
АдресКартинки = ПоместитьВоВременноеХранилище(ДвоичныеДанные, УникальныйИдентификатор);
Иначе
АдресКартинки = "";
КонецЕсли;
КонецПроцедуры
C:\Users\...) серверу недоступны. При загрузке с клиента файл необходимо сначала передать через ПоместитьФайлНаСервер() или ПоместитьВоВременноеХранилище(), а на сервере принять через ПолучитьДвоичныеДанныеИзВременногоХранилища().