В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью передать результат сложного запроса с сервера на клиент для последующей обработки или выгрузки в локальные файлы пользователя. Основная сложность заключается в том, что объект типа ТаблицаЗначений существует только в серверном контексте (или в толстом клиенте). При попытке передать его "напрямую" в управляемом приложении возникает ошибка преобразования данных XDTO, так как клиентский контекст просто "не знает", как интерпретировать этот тип данных.
В данной статье мы подробно разберем, как обойти это ограничение, рассмотрим наиболее эффективные способы передачи данных и выясним, как правильно формировать файлы Excel, избегая проблем с безопасностью и лицензированием.
Разберем причину возникновения ошибки "Ошибка преобразования данных XDTO". Платформа 1С использует механизм сериализации для передачи объектов между сервером и клиентом. ТаблицаЗначений является специализированным объектом базы данных, который содержит не только данные, но и методы их обработки (индексы, поиск, сворачивание). Эти методы не могут быть реализованы на стороне тонкого или веб-клиента. Поэтому при попытке возврата ТаблицаЗначений из функции с заголовком &НаСервере, платформа пытается упаковать её в пакет XDTO, не находит соответствующего типа для клиента и выдает ошибку.
Это самый популярный и интуитивно понятный способ. Суть его заключается в том, чтобы переложить данные из "недопустимого" типа в "универсальные" типы данных, такие как Массив и Структура, которые прекрасно сериализуются и доступны на клиенте.
Рассмотрим алгоритм реализации этого метода:
ТаблицаЗначений (например, результатом запроса).Массив, в который будем помещать строки.Структура, где ключи — это имена колонок, а значения — данные ячеек.Пример программного кода для преобразования:
&НаСервере
Функция ПолучитьДанныеДляЭкспорта()
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование ИЗ Справочник.Номенклатура");
ТЗ = Запрос.Выполнить().Выгрузить();
Результат = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из ТЗ.Колонки Цикл
СтруктураСтроки.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
КонецЦикла;
Результат.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат Результат;
КонецФункции
На клиенте мы можем легко обойти этот массив и заполнить COM-объект Excel:
&НаКлиенте
Процедура ВыгрузитьВExcel()
Данные = ПолучитьДанныеДляЭкспорта();
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.WorkBooks.Add();
Лист = Книга.WorkSheets(1);
НомерСтроки = 1;
Для Каждого СтрокаДанных Из Данные Цикл
Лист.Cells(НомерСтроки, 1).Value = СтрокаДанных.Наименование;
Лист.Cells(НомерСтроки, 2).Value = СтрокаДанных.Код;
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
Excel.Visible = Истина;
КонецПроцедуры
Если данных очень много (десятки тысяч строк), передача огромного массива структур может замедлить работу системы из-за долгой сериализации. В этом случае проанализируем использование Временного хранилища. Мы можем поместить ТаблицаЗначений в хранилище на сервере, передать на клиент только Адрес (строку), а затем порционно запрашивать данные с сервера.
Однако, как справедливо замечено в обсуждении, если мы все равно планируем использовать COM-объект на клиенте, частые вызовы сервера в цикле (по одной строке) будут крайне неэффективны из-за сетевых задержек. Поэтому метод с массивом структур остается оптимальным для большинства задач среднего масштаба.
Разберем наиболее современный и "правильный" с точки зрения архитектуры 1С способ. Вместо того чтобы мучительно передавать данные на клиент и поштучно заполнять ячейки через медленный COM-интерфейс, мы можем сформировать ТабличныйДокумент на сервере и сохранить его в формате XLSX.
Преимущества этого метода:
Проанализируем пример реализации:
&НаСервере
Функция СформироватьФайлНаСервере()
ТабДок = Новый ТабличныйДокумент;
// Заполняем ТабДок данными (можно использовать Построитель или просто вывод областей)
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование ИЗ Справочник.Номенклатура");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Здесь логика вывода строк в ТабДок
КонецЦикла;
ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
// Помещаем файл в хранилище, чтобы клиент мог его забрать
ДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
Адрес = ПоместитьВоВременноеХранилище(ДанныеФайла, ЭтаФорма.УникальныйИдентификатор);
Возврат Адрес;
КонецФункции
На стороне клиента нам остается только вызвать стандартный диалог сохранения файла или автоматически открыть его. Это решает проблему "безопасного режима", о которой упоминал автор темы: файлы в формате XLSX (начиная с 2007 стандарта) гораздо лояльнее воспринимаются системой безопасности Windows, чем старые XLS или HTML-файлы с расширением Excel.
Иногда бизнес-логика требует использования именно COM (например, использование сложных макросов или форматирования, недоступного в ТабличныйДокумент). Если вы решили создавать Excel.Application на сервере, вы можете столкнуться с ошибкой "Метод объекта не обнаружен WorkBooks".
Выясним причины и способы решения:
USR1CV8). У этого пользователя нет папок рабочего стола, которые необходимы Excel для запуска. Вы можете решить это, создав вручную папку Desktop по адресу: C:\Windows\System32\config\systemprofile\Desktop (и для SysWOW64 на 64-битных ОС).dcomcnfg для компонента Microsoft Excel Application, чтобы разрешить запуск и активацию от имени системной учетной записи.Важное замечание: Использование Microsoft Office на сервере официально не рекомендуется компанией Microsoft и может нарушать лицензионное соглашение. Всегда отдавайте предпочтение нативной выгрузке через ТабличныйДокумент в формат XLSX.
Подводя итог, проанализируем, какой метод выбрать:
ТипФайлаТабличногоДокумента.XLSX. Это избавит вас от зависимости от установленного ПО и проблем с лицензиями.Помните, что архитектура 1С 8.3 ориентирована на минимизацию объема передаваемых данных между сервером и клиентом. Старайтесь выполнять всю тяжелую обработку на стороне сервера, возвращая на клиент лишь готовый результат.