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

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке на управляемых формах в 1С:Предприятие часто возникает задача: сформировать отчет или печатную форму на сервере (где есть доступ к базе данных) и показать его пользователю на клиенте. Начинающие разработчики часто совершают ошибку, пытаясь сохранить результат в файл на сервере, ожидая, что клиентское приложение сможет его открыть. Давайте разберемся, почему так делать не стоит, и рассмотрим правильные способы решения этой задачи.

Суть проблемы: Файловая система сервера и клиента

В исходном коде мы видим, что автор пытается выполнить запрос на сервере, поместить данные в табличный документ, а затем сохранить этот документ во временный каталог:


ТП.Записать(КаталогВременныхФайлов +"Запрос.XLSX", ТипФайлаТабличногоДокумента.XLSX); 

Затем возникает вопрос: "Могу ли я теперь получить этот документ с клиента?". Ответ: Нет, напрямую не можете. Давайте проанализируем причины:

  1. Изоляция контекстов: Код, выполняемый директивой &НаСервере, работает на компьютере-сервере (в случае клиент-серверного варианта работы). Функция КаталогВременныхФайлов() вернет путь к папке Temp именно на сервере (например, C:\Windows\Temp\...).
  2. Клиентский доступ: Клиентское приложение (работающее на компьютере пользователя) не имеет прямого доступа к локальным дискам сервера. Оно не "видит" файлы, созданные сервером в его локальных папках.

Поэтому сохранять файл на диск сервера для передачи клиенту — это тупиковый путь, если не использовать механизмы передачи файлов через временное хранилище. Однако для передачи именно объекта ТабличныйДокумент существуют гораздо более простые и изящные способы.

Решение 1: Возврат объекта через Функцию (Рекомендуемый способ)

Как правильно заметили участники обсуждения, объект ТабличныйДокумент в платформе 1С является сериализуемым (XDTO-сериализация). Это означает, что он может "путешествовать" между сервером и клиентом напрямую через возвращаемое значение функции. Платформа сама упакует его в XML/JSON, передаст по сети и распакует на клиенте.

Давайте перепишем логику автора, превратив процедуру в функцию. Это самый простой способ, если отчет не весит сотни мегабайт.

Шаг 1. Создаем серверную функцию

Вместо процедуры &НаСервере, которая меняет данные формы, мы создадим функцию &НаСервереБезКонтекста (или &НаСервере, если нужен доступ к реквизитам формы), которая вернет готовый документ.


&НаСервере
Функция СформироватьОтчетНаСервере(НачалоПериода, КонецПериода)
    
    // 1. Создаем и заполняем ТабличныйДокумент
    ТабДок = Новый ТабличныйДокумент;
    
    // 2. Формируем запрос (используя консоль запросов)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   Товары.Номенклатура КАК Номенклатура,
        |   Док.Дата КАК Дата,
        |   Штрихкоды.Штрихкод КАК Штрихкод
        |ИЗ
        |   Документ.ПриобретениеТоваровУслуг.Товары КАК Товары
        |       ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриобретениеТоваровУслуг КАК Док
        |       ПО Товары.Ссылка = Док.Ссылка
        |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ШтрихкодыНоменклатуры КАК Штрихкоды
        |       ПО Товары.Номенклатура = Штрихкоды.Номенклатура
        |ГДЕ
        |   Док.Дата МЕЖДУ &ПериодНачало И &ПериодКонец";
        
    Запрос.УстановитьПараметр("ПериодНачало", НачалоПериода);
    Запрос.УстановитьПараметр("ПериодКонец", КонецПериода);
    
    // 3. Используем ПостроительОтчета (как в примере автора)
    // Хотя в современных решениях лучше использовать СКД или ручной вывод областей
    Построитель = Новый ПостроительОтчета;
    Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(Запрос.Выполнить().Выгрузить());
    
    // Выводим результат в наш ТабДок
    Построитель.Вывести(ТабДок);
    
    // 4. Возвращаем готовый объект на клиент
    Возврат ТабДок;
    
КонецФункции

Шаг 2. Вызываем функцию с клиента

Теперь на клиенте нам достаточно получить результат выполнения функции и показать его пользователю.


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

Этот метод идеально подходит для задач, где результат выборки не слишком велик. Мы избегаем работы с файловой системой и передаем данные в оперативной памяти.

Решение 2: Использование Временного Хранилища

Если отчет получается очень большим, передача его напрямую через возврат функции может "подвесить" интерфейс на время передачи данных, так как объем передаваемого трафика возрастет. В таких случаях, а также если нужно передать именно файл (например, Excel), используют механизм Временного хранилища.

Рассмотрим, как это работает:

  1. На сервере формируем документ.
  2. Помещаем его во временное хранилище с помощью метода ПоместитьВоВременноеХранилище.
  3. Получаем адрес (строку-ссылку).
  4. Возвращаем этот адрес на клиент.
  5. На клиенте получаем данные по адресу.

Пример реализации через временное хранилище:


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

&НаКлиенте
Процедура Сформировать(Команда)
    
    Адрес = ПоместитьОтчетВХранилище(Объект.НачалоПериода, Объект.КонецПериода);
    
    // Получаем документ из хранилища уже на клиенте
    // Важно: ПолучитьИзВременногоХранилища работает и на клиенте для простых типов, 
    // но для ТабДокумента надежнее использовать отображение через URL или методы работы с файлами.
    
    // Вариант А: Просто показать (если это ТабДок)
    // К сожалению, на клиенте ПолучитьИзВременногоХранилища имеет ограничения.
    // Поэтому для ТабДока чаще используется Решение №1.
    
    // Вариант Б: Если мы сохранили ТабДок как Excel во временное хранилище (как двоичные данные)
    // Тогда мы можем предложить пользователю скачать его.
    
КонецПроцедуры

Важное замечание: ТабличныйДокумент — это особый объект 1С. Метод с прямой передачей (Решение №1) работает лучше всего, так как платформа оптимизирована для этого. Метод с временным хранилищем чаще используется для передачи файлов (картинок, внешних отчетов, экспорта в изображения, Excel-файлов, сформированных как ДвоичныеДанные).

Анализ ошибок в коде автора

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

  1. Избыточность действий:

    
    результат = запрос.Выполнить.Выбрать; 
    пока результат.Следующий Цикл 
        // заполнение ТЧ 
    КонецЦикла;
    РезЗапроса = запрос.Выполнить.Выгрузить;
    

    Автор сначала перебирает выборку, чтобы заполнить табличную часть Объект.товары, а затем снова выполняет или выгружает запрос для ПостроительОтчета. Двойное обращение к результату запроса может быть ресурсоемким. Лучше выгрузить результат в Таблицу Значений один раз и использовать её как источник.

  2. Использование Построителя Отчета:

    
    ПостроительОтчета = Новый ПостроительОтчета;
    

    Объект ПостроительОтчета считается устаревшим механизмом (legacy), пришедшим из обычных форм. В управляемом приложении стандартом де-факто является Система Компоновки Данных (СКД). Она позволяет формировать отчеты с гораздо меньшим количеством кода и лучшей гибкостью настроек. Однако для простых "плоских" выводов можно использовать и прямой перебор выборки с выводом областей в макет.

  3. Попытка сохранить в XLSX на сервере:

    
    ТП.Записать(КаталогВременныхФайлов +"Запрос.XLSX", ...);
    

    Если ваша цель — отдать пользователю именно Excel-файл (или выполнить обратную операцию — загрузку из Excel), то алгоритм должен быть таким:

    1. Сформировать ТабличныйДокумент на сервере.
    2. Записать его во временный файл на сервере.
    3. Считать этот файл в ДвоичныеДанные.
    4. Поместить ДвоичныеДанные в ПоместитьВоВременноеХранилище.
    5. Удалить временный файл на сервере.
    6. Вернуть адрес хранилища на клиент.
    7. На клиенте использовать НачатьПолучениеФайла (или ПолучитьФайл), передав туда адрес.

Итог

Если вам нужно просто показать данные в 1С:

Это самый быстрый и надежный способ без лишних операций с файловой системой.

← На главную