При разработке функционала отправки электронных писем или работы с форматированным текстом в 1С:Предприятии 8.3 разработчики нередко сталкиваются с неожиданным поведением: после интерактивного изменения любых полей формы размер файла или двоичных данных вложенной картинки многократно увеличивается. Разберем детально механизм этого явления и рассмотрим способы оптимизации хранения данных.
Проанализируем типовой сценарий, в котором проявляется данное поведение платформы. Создадим управляемую форму со следующими реквизитами:
ФорматированныйДокумент (тип ФорматированныйДокумент), выведенный на форму с полем ввода и командной панелью.Флаг (тип Булево), выведенный в виде флажка (чекбокса).Посмотрим на поведение в отладчике. Сначала добавляем изображение в форматированный документ и проверяем размер двоичных данных картинки:
РазмерИсходный = ФорматированныйДокумент.Элементы[0].Элементы[0].Картинка.ПолучитьДвоичныеДанные().Размер();
На данном этапе метод возвращает ожидаемый объем, соответствующий исходному файлу (например, сжатому JPEG или PNG). Однако стоит переключить флажок на клиенте и снова обратиться к серверу с контекстом формы, как при следующем получении двоичных данных размер вырастает в несколько раз. Если же изменять значение реквизита исключительно на сервере без клиентской передачи, размер картинки остается неизменным.
Выясним причину такого поведения платформы 1С. Здесь действуют два ключевых фактора:
&НаСервере) платформа 1С сериализует все реквизиты формы, включая ФорматированныйДокумент.Картинка в составе контекста формы платформа не сохраняет оригинальный упакованный бинарный поток исходного файла со всеми его заголовками и метаданными. Вместо этого растровое изображение разворачивается во внутреннее несжатое представление (BMP/ARGB) либо перекодируется платформенным механизмом с параметрами по умолчанию. При последующем вызове метода ПолучитьДвоичныеДанные() на сервере возвращается уже поток пересобранного несжатого растра.Рассмотрим основной способ предотвращения лишней сериализации. Все вспомогательные серверные процедуры, которые вызываются при изменении реквизитов (флажков, полей ввода, списков), следует объявлять с директивой &НаСервереБезКонтекста. В этом случае реквизит ФорматированныйДокумент не передается по сети и не проходит цикл десериализации/пересборки растра.
Посмотрим на пример правильной организации обработчика изменения реквизита на клиенте:
&НаКлиенте
Процедура ФлагПриИзменении(Элемент)
// Передаем на сервер только необходимые примитивные данные
ОбработатьИзменениеФлагаНаСервере(Объект.Флаг);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбработатьИзменениеФлагаНаСервере(ЗначениеФлага)
// Выполняем серверную логику без пересылки контекста формы
// ФорматированныйДокумент не сериализуется
КонецПроцедуры
Если избежать контекстных вызовов невозможно и данные уже изменили свой внутренний формат, перед сохранением двоичных данных в файл, том или регистр сведений следует выполнить принудительное перекодирование объекта Картинка в сжатый формат.
&НаСервере
Процедура СохранитьКартинкуВТом(ИсходнаяКартинка, ПутьКФайлу)
// Преобразуем развернутый растр обратно в сжатый JPEG
КартинкаДляСохранения = ИсходнаяКартинка.Преобразовать(ФорматКартинки.JPEG);
ДвоичныеДанныеФайла = КартинкаДляСохранения.ПолучитьДвоичныеДанные();
ДвоичныеДанныеФайла.Записать(ПутьКФайлу);
КонецПроцедуры
Разберем архитектурный паттерн, используемый в «1С:Библиотеке стандартных подсистем» (БСП) для работы с исходящими электронными письмами. Вместо хранения "тяжелых" двоичных данных внутри тела форматированного документа применяется механизм извлечения изображений:
ПолучитьHTML() из форматированного документа извлекается текст и встроенные Base64-изображения.Content-ID (CID).<img src="data:image/..."> заменяются на <img src="cid:идентификатор">.Такой подход полностью исключает разрастание контекста формы и исключает проблемы с неконтролируемым увеличением размера писем при их повторном редактировании.