Представим, что в нашей конфигурации есть справочник "Товары":

Для каждого товара нужно иметь возможность загрузить в 1С его изображение, и показывать его на форме товара:

Задача с виду понятная и простая, но её реализация совсем неочевидная.
1. Как хранить изображение в 1С
Первое, что вы должны понимать: любой файл можно представить в виде последовательности байт, и изображение - не исключение:

Для хранения двоичных данных в 1С используется тип данных ХранилищеЗначения:

Для того чтобы загрузить изображение в 1С, нужно:
- получить двоичные данные изображения
- поместить двоичные данные в реквизит с типом ХранилищеЗначения

Тогда для выгрузки изображение из 1С в файл последовательность действий будет такой:
- из реквизита с типом ХранилищеЗначения получить двоичные данные файла
- записать двоичные данные в файл

Хранить двоичные данные файла в справочнике "Товары" не самая лучшая идея, потому что:
Рассмотрим реализацию с помощью отдельного справочника, назовём его ТоварыПрисоединенныеФайлы:
- это замедлит чтение данных из справочника: например, при программном получении данных объекта (метод ПолучитьОбъект()), или при обращении к реквизитам товара через точку (Товар.Артикул) из базы данных будут получены не только значения реквизитов справочника, но и картинка, а её размер может быть немаленьким
- кроме двоичиных данных файла важно хранить его расширение и другие сведения, например, размер, дату изменения, т.е. данные, которые напрямую к товару не имеют отношения
Рассмотрим реализацию с помощью отдельного справочника, назовём его ТоварыПрисоединенныеФайлы:

Для этого справочника установим в качестве владельца справочник Товары:

Теперь в справочник Товары добавим реквизит ФайлКартинки, в котором будем хранить ссылку на справочник присоединенных файлов:

2. Как показать картинку на форме
Чтобы показать в 1С картинку на форме товара нужно:
- создать реквизит формы АдресКартинки с типом Строка
- создать элемент формы вида Поле картинки, связанный с реквизитом АдресКартинки
- описать программный код заполнения реквизита АдресКартинки: его нужно заполнить либо адресом во временном хранилище, по которому находятся двоичные данные картинки, либо навигационной ссылкой на реквизит, в котором хранятся двоичные данные картинки

Теперь поработаем с кодом.
Обработаем событие Нажатие, связанное с полем картинки:
Обработаем событие Нажатие, связанное с полем картинки:

При нажатии на поле картинки:
- если картинка уже выбрана, будем показывать её пользователю
- если картинка ещё не выбрана, будем показывать диалог для выбора картинки, а потом выбранную картинку помещать во временное хранилище
&НаКлиенте
Процедура АдресКартинкиНажатие(Элемент, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Если ЗначениеЗаполнено(Объект.ФайлКартинки) Тогда
ПоказатьКартинку();
Иначе
ВыбратьКартинку();
КонецЕсли;
КонецПроцедуры
Код процедуры ВыбратьКартинку():
&НаКлиенте
Асинх Процедура ВыбратьКартинку()
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите картинку";
ПараметрыДиалога.Фильтр = "Все файлы изображений | *.jpg; *.png; *.bmp";
ОписаниеФайла = Ждать ПоместитьФайлНаСерверАсинх(,,,ПараметрыДиалога,УникальныйИдентификатор);
Если ОписаниеФайла <> Неопределено Тогда
АдресКартинки = ОписаниеФайла.Адрес;
ИнформацияОФайле = Новый Структура;
ИнформацияОФайле.Вставить("Адрес", АдресКартинки);
ИнформацияОФайле.Вставить("Имя", ОписаниеФайла.СсылкаНаФайл.Имя);
ИнформацияОФайле.Вставить("Расширение", ОписаниеФайла.СсылкаНаФайл.Расширение);
ИнформацияОФайле.Вставить("Размер", ОписаниеФайла.СсылкаНаФайл.Размер());
Объект.ФайлКартинки = СоздатьПрисоединенныйФайл(Объект.Ссылка, ИнформацияОФайле);
КонецЕсли;
КонецПроцедуры
Для помещения файла во временное хранилище с предварительным показом диалога выбора файла в приведенном выше коде используется асинхронный метод ПоместитьФайлНаСерверАсинх().
После помещения файла на сервер, адрес, по которому находятся данные картинки во временном хранилище помещается в реквизит формы АдресКартинки, а затем вызывается функция, в которой происходит создание элемента в справочнике ТоварыПрисоединенныеФайлы, ниже приведен код этой функции:
После помещения файла на сервер, адрес, по которому находятся данные картинки во временном хранилище помещается в реквизит формы АдресКартинки, а затем вызывается функция, в которой происходит создание элемента в справочнике ТоварыПрисоединенныеФайлы, ниже приведен код этой функции:
&НаСервереБезКонтекста
Функция СоздатьПрисоединенныйФайл(Товар, ИнформацияОФайле)
ХранилищеКартинки = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(ИнформацияОФайле.Адрес),
Новый СжатиеДанных(9));
ОбъектФайла = Справочники.ТоварыПрисоединенныеФайлы.СоздатьЭлемент();
ОбъектФайла.Владелец = Товар;
ОбъектФайла.Наименование = ИнформацияОФайле.Имя;
ОбъектФайла.Расширение = ИнформацияОФайле.Расширение;
ОбъектФайла.Размер = ИнформацияОФайле.Размер;
ОбъектФайла.ДанныеФайла = ХранилищеКартинки;
ОбъектФайла.ИмяФайла = СтрЗаменить(ОбъектФайла.Наименование, ОбъектФайла.Расширение, "");
ОбъектФайла.Записать();
Возврат ОбъектФайла.Ссылка;
КонецФункции // СоздатьПрисоединенныйФайл()
Если картинка товара уже выбрана, то при нажатии на поле картинки вызывается процедура ПоказатьКартинку(), ниже её код:
&НаКлиенте
Асинх Процедура ПоказатьКартинку()
ДанныеКартинки = ПолучитьДанныеКартинки(Объект.ФайлКартинки);
ПутьКФайлу = ПолучитьИмяВременногоФайла(ДанныеКартинки.Расширение);
Ждать ПолучитьФайлССервераАсинх(ДанныеКартинки.АдресДанных, ПутьКФайлу);
Попытка
ЗапуститьПриложениеАсинх(ПутьКФайлу);
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не удалось открыть файл картинки по причине: " + ОписаниеОшибки();
Сообщение.Сообщить();
КонецПопытки;
КонецПроцедуры // ПоказатьКартинку()
С помощью функции ПолучитьДанныеКартинки() из базы данных выбираются сведения о картинке - Наименование, Расширение, Размер и, самое главное, Адрес во временном хранилище, по которому находятся двоичные данные файла:
&НаСервереБезКонтекста
Функция ПолучитьДанныеКартинки(ФайлКартинки)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыПрисоединенныеФайлы.Наименование КАК Наименование,
| ТоварыПрисоединенныеФайлы.ДанныеФайла КАК АдресДанных,
| ТоварыПрисоединенныеФайлы.ИмяФайла КАК ИмяФайла,
| ТоварыПрисоединенныеФайлы.Расширение КАК Расширение,
| ТоварыПрисоединенныеФайлы.Размер КАК Размер
|ИЗ
| Справочник.ТоварыПрисоединенныеФайлы КАК ТоварыПрисоединенныеФайлы
|ГДЕ
| ТоварыПрисоединенныеФайлы.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ФайлКартинки);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
СтруктураДанных = Новый Структура;
Для каждого Колонка Из РезультатЗапроса.Колонки Цикл
ИмяКолонки = Колонка.Имя;
Если ИмяКолонки = "АдресДанных" Тогда
ЗначениеКолонки = ПоместитьВоВременноеХранилище(Выборка[ИмяКолонки].Получить());
Иначе
ЗначениеКолонки = Выборка[ИмяКолонки];
КонецЕсли;
СтруктураДанных.Вставить(ИмяКолонки, ЗначениеКолонки);
КонецЦикла;
Возврат СтруктураДанных;
КонецФункции // ПолучитьДанныеКартинки()
Теперь мы видим на форме картинку:

Но этого недостаточно: если теперь закрыть форму товара, а потом открыть снова, картинка пропадет:

Это связано с тем, что при открытии формы товара реквизит АдресКартинки сейчас не заполняется. Исправим это: создадим обработчик события формы ПриЧтенииНаСервере, и в нём будем заполнять реквизит АдресКартинки навигационной ссылкой на реквизит "ДанныеФайла" справочника ТоварыПрисоединенныеФайлы:

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
АдресКартинки = ПолучитьНавигационнуюСсылку(ТекущийОбъект.ФайлКартинки, "ДанныеФайла");
КонецПроцедуры
Теперь при повторном открытии товара изображение на форме отображается:

Добавим возможность удаления изображения товара. Для этого создадим команду формы УдалитьИзображение, и расположим её в контекстном меню поля картинки:

В коде обработчика команды будем помечать на удаление элемент справочника присоединенных файлов, в котором хранятся двоичные данные изображения, а также очистить реквизит формы АдресКартинки и реквизит объекта (товара) ФайлКартинки:
&НаКлиенте
Процедура УдалитьИзображение(Команда)
ПометитьНаУдалениеПрисоединенныйФайл(Объект.ФайлКартинки);
Объект.ФайлКартинки = Неопределено;
АдресКартинки = "";
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПометитьНаУдалениеПрисоединенныйФайл(ПрисоединенныйФайл)
Если НЕ ЗначениеЗаполнено(ПрисоединенныйФайл) Тогда
Возврат;
КонецЕсли;
ПрисоединенныйФайлОбъект = ПрисоединенныйФайл.ПолучитьОбъект();
ПрисоединенныйФайлОбъект.УстановитьПометкуУдаления(Истина);
ПрисоединенныйФайлОбъект.Записать();
КонецПроцедуры
Готово, теперь пользователь может удалить изображение:

По ссылке ниже вы можете скачать выгрузку демонстрационной информационной базы, в которой реализован пример, описанный выше: https://clck.ru/34d5vK
Присоединяйтесь к нам в Telegram:
https://t.me/ironskills_community1c
Если вы хотите разобраться во всех ключевых механизмах платформы, научиться разрабатывать собственные конфигурации 1С и дорабатывать существующие, приходите на наш комплексный курс по программированию в 1С: Узнать подробнее
https://t.me/ironskills_community1c
Если вы хотите разобраться во всех ключевых механизмах платформы, научиться разрабатывать собственные конфигурации 1С и дорабатывать существующие, приходите на наш комплексный курс по программированию в 1С: Узнать подробнее