Как загрузить табличный документ из файла и отобразить его на управляемой форме 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача хранения пользовательских макетов или отчетов непосредственно в базе данных. Типичный сценарий: пользователь выбирает файл .mxl на диске, программа сохраняет его в справочник, а при открытии карточки справочника этот документ должен отобразиться в окне предварительного просмотра на форме. Разберем детально, как реализовать этот механизм, избежав типичных ошибок новичков.

Шаг 1: Выбор файла и передача данных на сервер

Проанализируем первый этап работы. Нам необходимо предоставить пользователю возможность выбрать файл на локальном компьютере. Для этого в управляемом приложении используется объект ДиалогВыбораФайла. Рассмотрим код процедуры, которая инициирует процесс загрузки на клиенте:


&НаКлиенте
Процедура ЗагрузитьМакет(Команда)
    длгВыборФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    длгВыборФайла.Заголовок = "Выберите табличный документ...";
    длгВыборФайла.Фильтр = "Табличный документ (*.mxl)|*.mxl";
    длгВыборФайла.Расширение = "mxl";
    длгВыборФайла.ПроверятьСуществованиеФайла = Истина;

    Если Не длгВыборФайла.Выбрать() Тогда 
        Возврат; 
    КонецЕсли;

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

Здесь важно обратить внимание на использование функции ПоместитьВоВременноеХранилище. Мы передаем второй параметр — УникальныйИдентификатор формы. Это гарантирует, что данные будут «жить» в хранилище до тех пор, пока открыта форма, и будут автоматически удалены после её закрытия.

Шаг 2: Обработка данных на сервере и сохранение в ХранилищеЗначения

Теперь рассмотрим ситуацию на сервере. Получив адрес временного хранилища, нам нужно превратить двоичные данные в объект ТабличныйДокумент и сохранить его в реквизит объекта (справочника), имеющий тип ХранилищеЗначения. Проанализируем правильный подход к этой задаче:


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

В данном примере мы использовали объект СжатиеДанных со степенью 9. Так как ТабличныйДокумент внутри представляет собой XML-структуру, он очень хорошо сжимается, что позволяет существенно экономить место в базе данных при хранении больших макетов.

Шаг 3: Отображение документа при открытии формы

Выясним причину, по которой у многих разработчиков не получается отобразить сохраненный документ при повторном открытии карточки. Проблема заключается в том, что реквизит с типом ХранилищеЗначения не считывается автоматически в данные формы. Нам необходимо программно извлечь его в обработчике ПриСозданииНаСервере.

Предположим, на вашей форме есть реквизит с именем РезультатПросмотра, имеющий тип ТабличныйДокумент. Посмотрим на алгоритм его заполнения:


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

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

Оптимизация: Использование потоков (MemoryStream)

Разберем более современный способ, доступный в версиях платформы 8.3.9 и выше. Использование временных файлов на диске — это всегда риск (ошибки прав доступа, заполнение места, медленная работа HDD). Мы можем заменить дисковые операции работой в оперативной памяти с помощью потоков.

Проанализируем пример чтения табличного документа через ПотокВПамяти:


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

Этот метод работает значительно быстрее и надежнее, так как исключает взаимодействие с файловой системой сервера.

Важные рекомендации при работе с макетами

Рассмотрим несколько нюансов, которые помогут сделать ваше решение профессиональным:

  1. Проверка формата: Всегда используйте явное указание СпособЧтенияТабличногоДокумента.MXL в методе Прочитать(). Это предотвратит ошибки, если временный файл будет иметь некорректное расширение.
  2. Веб-клиент: Если ваша база работает через браузер, помните, что метод Новый ДвоичныеДанные(ПутьКФайлу) на клиенте может не сработать. В этом случае используйте асинхронный метод НачатьПомещениеФайлаНаСервер().
  3. Объем данных: Если табличные документы очень объемные (десятки мегабайт), хранение их в основном справочнике может замедлить работу системы. В таких случаях рассмотрите возможность использования Библиотеки стандартных подсистем (БСП), а именно подсистемы «Работа с файлами», которая позволяет выносить данные в отдельные таблицы или тома на диске.

Подведем итог: для успешной выгрузки и отображения данных необходимо правильно организовать цепочку: Файл -> ДвоичныеДанные -> ТабличныйДокумент -> ХранилищеЗначения на этапе сохранения, и обратную цепочку ХранилищеЗначения -> ТабличныйДокумент -> Реквизит формы при чтении. Следуя этим инструкциям, вы создадите надежный механизм работы с пользовательскими макетами.

← На главную