При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью вывода отчетов или документов, содержащих большое количество графической информации. Типичная ситуация: на сервере формируется ТабличныйДокумент, в него программно добавляются изображения (например, сканы документов или фотографии товаров), и этот объект возвращается на клиент для отображения пользователю. Однако, когда суммарный объем изображений превышает определенный порог (в нашем случае около 130 МБ), система выдает критическую ошибку преобразования данных XDTO.
В этой статье мы подробно разберем, почему возникает ошибка отображения типа base64Binary в тип ДвоичныеДанные, проанализируем архитектурные ограничения платформы и рассмотрим наиболее эффективные способы решения этой задачи.
Разберем ситуацию с технической точки зрения. Когда мы вызываем функцию с директивой &НаСервере и возвращаем из нее объект типа ТабличныйДокумент, платформа выполняет автоматическую сериализацию этого объекта в формат XML для передачи по протоколу HTTP. В этот момент происходят следующие процессы:
Ошибка отображения типов часто связана с тем, что система не может выделить непрерывный блок памяти для обработки гигантской строки Base64, особенно если используется 32-битный тонкий клиент.Проанализируем исходный код, который приводит к ошибке:
&НаСервере
Функция ПосмотретьСканыНаСервере()
ТабДок = Новый ТабличныйДокумент;
// ... логика заполнения документа картинками
Возврат ТабДок; // Ошибка происходит здесь при передаче на клиент
КонецФункции
&НаКлиенте
Процедура ПосмотретьСканы(Команда)
ТабДок = ПосмотретьСканыНаСервере();
Если ТабДок <> Неопределено Тогда
ТабДок.Показать();
КонецЕсли;
КонецПроцедуры
Рассмотрим подробнее способы обхода этой проблемы, которые позволяют передавать данные стабильно и быстро.
Первый и наиболее простой способ оптимизации — исключить прямую передачу тяжелого объекта как возвращаемого значения функции. Вместо этого мы можем поместить сформированный ТабличныйДокумент во временное хранилище на сервере и передать на клиент только адрес (строку-идентификатор).
Выясним, почему это работает лучше. При использовании ПоместитьВоВременноеХранилище() данные передаются в специализированный кэш сервера, а на клиент возвращается лишь короткая ссылка. Последующее получение данных на клиенте через ПолучитьИзВременногоХранилища() во многих версиях платформы оптимизировано и менее чувствительно к лимитам XDTO-сериализатора.
Реализуем этот подход по шагам:
&НаСервере
Функция СформироватьТабДокВоВременноеХранилище()
ТабДок = Новый ТабличныйДокумент;
// ... цикл заполнения ТабДок картинками
// Сохраняем объект и получаем адрес
АдресХранилища = ПоместитьВоВременноеХранилище(ТабДок, ЭтаФорма.УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
&НаКлиенте
Процедура ПосмотретьСканы(Команда)
Адрес = СформироватьТабДокВоВременноеХранилище();
// На клиенте получаем объект по адресу
НашТабДок = ПолучитьИзВременногоХранилища(Адрес);
НашТабДок.Показать("Просмотр изображений");
КонецПроцедуры
Важный нюанс: Обязательно передавайте УникальныйИдентификатор формы во второй параметр функции ПоместитьВоВременноеХранилище(). Это гарантирует, что данные будут жить ровно столько, сколько открыта форма, и будут автоматически удалены после её закрытия, не забивая память сервера.
Если первый способ не помогает (например, из-за крайне жестких ограничений по памяти на клиенте), воспользуемся самым надежным методом — передачей через формат MXL в виде ДвоичныхДанных. Этот метод считается «золотым стандартом» при работе с очень тяжелыми отчетами.
Проанализируем преимущества этого метода:
.mxl при записи на диск является сжатым (внутренняя структура напоминает ZIP).ДвоичныеДанные передаются потоком, что гораздо эффективнее для памяти, чем Base64-текст внутри XML.Разберем реализацию этого алгоритма:
&НаСервере
Функция ПолучитьТабДокКакДвоичныеДанные()
ТабДок = Новый ТабличныйДокумент;
// ... заполнение документа
// Создаем временное имя файла на сервере
ИмяФайла = ПолучитьИмяВременногоФайла("mxl");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.MXL);
// Читаем файл в объект ДвоичныеДанные
Данные = Новый ДвоичныеДанные(ИмяФайла);
// Удаляем временный файл с сервера сразу после прочтения
УдалитьФайлы(ИмяФайла);
Возврат ПоместитьВоВременноеХранилище(Данные);
КонецФункции
&НаКлиенте
Процедура ПосмотретьСканы(Команда)
Адрес = ПолучитьТабДокКакДвоичныеДанные();
ДанныеССервера = ПолучитьИзВременногоХранилища(Адрес);
// На клиенте записываем данные во временный файл
ИмяФайлаКлиент = ПолучитьИмяВременногоФайла("mxl");
ДанныеССервера.Записать(ИмяФайлаКлиент);
// Читаем в табличный документ
НовыйТабДок = Новый ТабличныйДокумент;
НовыйТабДок.Прочитать(ИмяФайлаКлиент);
НовыйТабДок.Показать();
// Не забываем за собой чистить
УдалитьФайлы(ИмяФайлаКлиент);
КонецПроцедуры
Помимо способов передачи данных, важно обратить внимание на то, как именно картинки попадают в ТабличныйДокумент. Если мы используем оригинальные фотографии весом по 5–10 МБ каждая, мы быстро исчерпаем ресурсы системы.
Рассмотрим несколько советов по оптимизации графики на сервере:
Рисунок.Картинка, попробуйте изменить её разрешение. В 1С для этого можно использовать объект ОбработкаКартинок (в новых версиях) или внешние библиотеки. Для просмотра на экране монитора часто достаточно разрешения 1024x768.Картинка и предотвращает ошибки десериализации.Подводя итог, можно сказать, что проблема «Ошибка преобразования данных XDTO» при передаче табличного документа — это сигнал о том, что объем данных превысил возможности стандартного механизма передачи. Используя временное хранилище или MXL-контейнеры, вы сможете обойти эти ограничения и обеспечить стабильную работу вашей информационной системы.