Картинки и файлы во встроенном языке 1С 8.3: загрузка, хранение, вывод в форму и отчёт

· 10 мин

Содержание

Чтобы программно установить картинку в 1С 8.3, получите её двоичные данные, поместите их во временное хранилище через ПоместитьВоВременноеХранилище() и присвойте полученный адрес реквизиту формы, к которому привязано поле картинки. Для хранения в базе те же данные кладут в реквизит типа ХранилищеЗначения. Объект Картинка нужен для макетов, кнопок и проверки формата.

Частая ошибка новичков — записать в реквизит объекта сам объект Картинка или путь к файлу на диске пользователя. Первое платформа не позволит: тип Картинка нельзя выбрать для реквизита справочника или документа. Второе сломается на соседнем компьютере, где такого файла нет. Всё, что описано ниже, относится к управляемым формам платформы 8.3 и не зависит от конфигурации.

Какие типы данных отвечают за графику

Работа с изображением в 1С всегда идёт через несколько типов. Каждый решает свою задачу, и путаница между ними — главный источник ошибок.

ТипГде живётДля чего нужен
ДвоичныеДанныеклиент и сервер«сырые» байты файла: PNG, JPEG, PDF, что угодно
Картинкаклиент и серверизображение, которое понимает платформа: макет, кнопка, рисунок
ХранилищеЗначенияреквизит объекта в базеупаковка двоичных данных для записи в справочник или документ
Строка — адрес во временном хранилищереквизит формыпередача данных между клиентом и сервером, источник для поля картинки
ФорматКартинкиперечислениепроверка формата: PNG, JPEG, GIF, BMP и другие

Переходы между типами выглядят так:

  • 📥 из файла получаем ДвоичныеДанные;
  • 🖼 Новый Картинка(ДвоичныеДанные) — превращаем байты в картинку;
  • 📤 Картинка.ПолучитьДвоичныеДанные() — обратно в байты;
  • 💾 Новый ХранилищеЗначения(ДвоичныеДанные) — упаковка для записи, Хранилище.Получить() — распаковка;
  • 🔗 ПоместитьВоВременноеХранилище() и ПолучитьИзВременногоХранилища() — передача через адрес.

Для картинок из конфигурации (общих картинок в дереве метаданных) двоичные данные не нужны. Их берут напрямую: БиблиотекаКартинок.ИмяКартинки.

Как загрузить картинку из файла программно

Источник файла определяет, где выполняется код. Файл с компьютера пользователя доступен только на клиенте. Файл из сетевой папки или по URL удобнее читать на сервере.

С компьютера пользователя через диалог

Клиент не может просто «открыть» локальный файл на сервере. Файл сначала передают во временное хранилище, а сервер забирает его оттуда по адресу.

&НаКлиенте
Процедура ЗагрузитьФото(Команда)
	Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
	НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
	Если Не Результат Тогда
		Возврат; // пользователь закрыл диалог
	КонецЕсли;
	Расширение = НРег(Прав(ВыбранноеИмяФайла, 4));
	Если СтрНайти(".png|.jpg|jpeg|.gif|.bmp", Расширение) = 0 Тогда
		ПоказатьПредупреждение(, "Выберите файл изображения: PNG, JPG, GIF или BMP.");
		Возврат;
	КонецЕсли;
	ЗагрузитьФотоНаСервере(Адрес);
КонецПроцедуры

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

Здесь Объект.Фото — реквизит справочника типа ХранилищеЗначения, а АдресФото — строковый реквизит формы, к которому привязано поле картинки. Четвёртый параметр Истина означает интерактивный выбор, а УникальныйИдентификатор формы привязывает время жизни данных к открытой форме.

С диска сервера

Если картинки лежат в общей папке, читайте их на сервере конструктором двоичных данных:

&НаСервере
Функция ПрочитатьКартинкуССервера(ПутьКФайлу)
	Файл = Новый Файл(ПутьКФайлу);
	Если Не Файл.Существует() Тогда
		ВызватьИсключение СтрШаблон("Файл не найден: %1", ПутьКФайлу);
	КонецЕсли;
	Возврат Новый ДвоичныеДанные(ПутьКФайлу);
КонецФункции

В клиент-серверном варианте путь относится к компьютеру с сервером 1С, а не к рабочему месту. Права на папку нужны пользователю, от имени которого запущена служба сервера. Сетевую папку указывайте в формате UNC: \\fileserver\photo\001.jpg, подключённые диски службе не видны.

По ссылке из интернета

&НаСервере
Функция СкачатьКартинку(Сервер, ПутьНаСервере)
	Соединение = Новый HTTPСоединение(Сервер, 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL);
	Запрос = Новый HTTPЗапрос(ПутьНаСервере);
	Ответ = Соединение.Получить(Запрос);
	Если Ответ.КодСостояния <> 200 Тогда
		ВызватьИсключение СтрШаблон("Сервер вернул код %1", Формат(Ответ.КодСостояния, "ЧГ=0"));
	КонецЕсли;
	Возврат Ответ.ПолучитьТелоКакДвоичныеДанные();
КонецФункции

Вызов: СкачатьКартинку("example.com", "/images/logo.png"). Таймаут 30 секунд защищает от зависшего сеанса, если удалённый сайт не отвечает.

Где хранить изображение и как прикрепить файл к документу

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

СпособКогда подходитРиски
Реквизит ХранилищеЗначения в самом объектеодно фото на элемент: товар, сотрудник, логотипобъект становится тяжёлым, каждое чтение тянет картинку
Отдельный справочник «Изображения» с владельцемнесколько файлов на элемент, их надо добавлять и удалятьнужна своя форма и код удаления
Присоединённые файлы БСПтиповая конфигурация, нужны версии, тома, правапридётся разобраться с программным интерфейсом библиотеки

Запись в реквизит объекта

Если реквизит заполняется не из формы, а, например, при загрузке справочника, код такой:

ТоварОбъект = Товар.ПолучитьОбъект();
ТоварОбъект.Фото = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
ТоварОбъект.Записать();

Прочитать обратно: ДвоичныеДанные = Товар.Фото.Получить();. Если картинку ни разу не записывали, Получить() вернёт Неопределено, и это надо проверять.

Присоединённый файл в типовой конфигурации

В конфигурациях на БСП 3.x (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) к документу файл прикрепляют через общий модуль РаботаСФайлами. Сначала данные кладут во временное хранилище, потом создают файл:

ПараметрыФайла = РаботаСФайлами.ПараметрыДобавленияФайла();
ПараметрыФайла.ВладелецФайлов = ДокументСсылка;
ПараметрыФайла.ИмяБезРасширения = "Скан договора";
ПараметрыФайла.РасширениеБезТочки = "jpg";

Адрес = ПоместитьВоВременноеХранилище(ДвоичныеДанные);
ФайлСсылка = РаботаСФайлами.ДобавитьФайл(ПараметрыФайла, Адрес);

Плюс такого пути: файл появится в стандартной панели присоединённых файлов, попадёт в тома, если они настроены, и будет подчиняться правам. Состав полей структуры параметров зависит от версии БСП — откройте функцию ПараметрыДобавленияФайла в конфигураторе и посмотрите комментарий к ней.

Как показать картинку на форме программно

Поле картинки на управляемой форме не умеет показывать ХранилищеЗначения напрямую. Ему нужен строковый реквизит с адресом во временном хранилище или навигационной ссылкой.

Подготовка формы

  1. Добавьте реквизит формы АдресФото с типом Строка без ограничения длины.
  2. Перетащите его на форму. В свойстве элемента Вид выберите Поле картинки.
  3. В свойстве Размер картинки поставьте Пропорционально, чтобы фото не искажалось.
  4. Заполните Текст невыбранной картинки, например «Фото не загружено».

Заполнение при открытии

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	ОбновитьКартинку();
КонецПроцедуры

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

Для уже записанного объекта есть второй вариант без промежуточного хранилища:

АдресФото = ПолучитьНавигационнуюСсылку(Объект.Ссылка, "Фото");

Он экономит память сервера, но работает только для записанного элемента. В новом, ещё не записанном объекте ссылки нет — используйте временное хранилище.

Картинка на кнопке или декорации

Для элементов оформления данные из базы обычно не нужны:

Элементы.КнопкаПечать.Картинка = БиблиотекаКартинок.Печать;
Элементы.ДекорацияЛоготип.Картинка = Новый Картинка(ДвоичныеДанные);

Имя после БиблиотекаКартинок — это имя общей картинки в ветке Общие → Общие картинки. Код выполняется на сервере, например в ПриСозданииНаСервере, или на клиенте, если данные уже там.

Вывод изображения в отчёт и печатную форму

В табличном документе картинка выводится через рисунок в макете. Это основной способ для печатных форм: карточки товара, бейджа, акта с фото.

Через рисунок в макете

  1. В макете выделите область, например Шапка.
  2. Вставьте рисунок через меню Таблица → Рисунки → Картинка и задайте ему имя, скажем Фото.
  3. В коде заполните рисунок перед выводом области:
Макет = ПолучитьМакет("КарточкаТовара");
Область = Макет.ПолучитьОбласть("Шапка");
ДвоичныеДанные = Товар.Фото.Получить();
Если ДвоичныеДанные <> Неопределено Тогда
	Область.Рисунки.Фото.Картинка = Новый Картинка(ДвоичныеДанные);
КонецЕсли;
ТабДок.Вывести(Область);

Если фото у элемента нет, рисунок останется пустым. Чтобы не было пустой рамки, задайте у рисунка в макете прозрачную линию или удалите его из области: Область.Рисунки.Удалить(Область.Рисунки.Фото).

Рисунок без макета

Можно добавить рисунок в готовый табличный документ напрямую:

Рисунок = ТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
Рисунок.Картинка = Новый Картинка(ДвоичныеДанные);
Рисунок.Расположить(ТабДок.Область("R2C2:R10C5"));

Метод Расположить() вписывает рисунок в указанный диапазон ячеек. Для отчёта на СКД картинку обычно добавляют уже в полученный табличный документ, после вывода результата компоновки.

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

Генерация картинки средствами встроенного языка

Иногда изображения нет в файле, его нужно создать: QR-код для чека, график для письма, картинку из строки Base64, пришедшей в JSON.

Из строки Base64

Внешние сервисы часто присылают изображение строкой. Превратить её в картинку можно одной строкой кода:

ДвоичныеДанные = Base64Значение(СтрокаBase64);
Картинка = Новый Картинка(ДвоичныеДанные);

Если строка начинается с префикса вида data:image/png;base64,, отрежьте всё до запятой включительно, иначе Base64Значение() вернёт пустые данные.

Из диаграммы

Объект Диаграмма можно заполнить на сервере и снять с него картинку заданного размера в пикселях:

Картинка = Диаграмма.ПолучитьКартинку(800, 400);
ДвоичныеДанные = Картинка.ПолучитьДвоичныеДанные();

Дальше такие данные можно вложить в письмо, сохранить в хранилище или вывести в макет.

QR-код и штрихкод

В конфигурациях на БСП 3.1 и новее есть общий модуль ГенерацияШтрихкода. Его функция ИзображениеШтрихкода() принимает структуру параметров и возвращает структуру с готовой картинкой и двоичными данными. Параметры получают функцией ГенерацияШтрихкода.ПараметрыГенерацииШтрихкода(), заполняют ширину, высоту, тип кода и сам текст. Набор полей и допустимые типы смотрите в комментарии к функции в своей версии библиотеки.

Размер изображений и как не раздуть базу

Каждое фото в ХранилищеЗначения хранится в той же таблице, что и объект. Тысяча товаров по 5 МБ — это 5 ГБ, которые попадут в каждую выгрузку .dt и каждый бэкап.

Что реально помогает:

  • 📏 Ограничьте размер на входе. Проверка ДвоичныеДанные.Размер() > 2 * 1024 * 1024 отсекает файлы больше 2 МБ (2 097 152 байта).
  • 🗜 Новый СжатиеДанных(9) в конструкторе хранилища почти не уменьшает JPEG и PNG: эти форматы уже сжаты. Выигрыш есть для BMP и TIFF.
  • 🖼 Просите пользователей загружать JPEG вместо BMP и снимков с телефона в полном разрешении. Фото товара 1000×1000 точек в JPEG обычно занимает 100–300 КБ.
  • 📦 Выносите картинки из основного объекта в отдельный справочник. Тогда открытие списка товаров не тянет за собой мегабайты данных.
  • 💽 Для больших объёмов используйте хранение файлов в томах на диске — в типовых конфигурациях это настройка присоединённых файлов в разделе Администрирование.

Временное хранилище тоже расходует память сервера. Когда адрес больше не нужен, освобождайте его: УдалитьИзВременногоХранилища(Адрес). Особенно это касается обработок, которые в цикле загружают сотни файлов.

Защитный код: проверки и обработка исключений

Битый файл, переименованный документ Word с расширением .jpg, пропавшая сетевая папка — всё это должно заканчиваться понятным сообщением, а не падением формы.

Проверка, что файл действительно изображение

&НаСервере
Функция ЭтоКартинка(ДвоичныеДанные)
	Если ТипЗнч(ДвоичныеДанные) <> Тип("ДвоичныеДанные") Или ДвоичныеДанные.Размер() = 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	Попытка
		Картинка = Новый Картинка(ДвоичныеДанные);
		Возврат Картинка.Формат() <> ФорматКартинки.НеизвестныйФормат;
	Исключение
		Возврат Ложь;
	КонецПопытки;
КонецФункции

Проверка по расширению в имени файла — только первый фильтр. Окончательно формат определяет Картинка.Формат().

Запись ошибки в журнал регистрации

Попытка
	ДвоичныеДанные = СкачатьКартинку(Сервер, Путь);
Исключение
	ЗаписьЖурналаРегистрации("ЗагрузкаКартинок", УровеньЖурналаРегистрации.Ошибка, , ,
		ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	ОбщегоНазначения.СообщитьПользователю("Не удалось загрузить изображение. Подробности в журнале регистрации.");
	Возврат;
КонецПопытки;

ОбщегоНазначения.СообщитьПользователю есть в конфигурациях на БСП. В самописной базе замените его на Сообщить() или объект СообщениеПользователю.

Частые ошибки и как их избежать

Диагностика: выберите симптом

Частые вопросы

Можно ли сделать реквизит справочника типа «Картинка»?
Нет. Для реквизитов объектов этот тип недоступен. Храните изображение в реквизите типа ХранилищеЗначения, а объект Картинка создавайте из полученных данных при выводе.
Какие форматы изображений понимает платформа?
Основные растровые форматы: PNG, JPEG, GIF, BMP, TIFF, а также ICO и метафайлы WMF и EMF. Список значений есть в перечислении ФорматКартинки в синтакс-помощнике.
Как выгрузить картинку из базы обратно в файл?
На сервере вызовите ДвоичныеДанные.Записать(ПутьКФайлу). Для сохранения на компьютер пользователя поместите данные во временное хранилище и на клиенте вызовите ПолучитьФайл или его асинхронный вариант.
Почему фото в поле картинки растянуто или обрезано?
У элемента формы задан неподходящий размер картинки. Поставьте значение «Пропорционально» в свойстве элемента или программно через свойство РазмерКартинки.
Работает ли этот код в веб-клиенте?
Да. Загрузка через НачатьПомещениеФайла и показ через адрес во временном хранилище работают в тонком и веб-клиенте одинаково. Не работает только прямое чтение локальных файлов кодом на клиенте без диалога.