В процессе разработки на платформе 1С:Предприятие 8.3 часто возникает задача программно открыть файл табличного документа (.mxl) так, чтобы это выглядело для пользователя максимально естественно. Под «естественностью» мы понимаем не просто отображение данных, но и сохранение связи с исходным файлом на диске. Если пользователь нажмет кнопку «Сохранить» или комбинацию клавиш Ctrl+S, система должна обновить существующий файл, а не предлагать выбрать путь заново. Рассмотрим, как реализовать этот функционал, обходя ограничения клиент-серверной архитектуры.
Когда мы работаем в тонком клиенте, у нас нет возможности напрямую вызвать метод Прочитать() объекта ТабличныйДокумент, указав путь к файлу на локальном компьютере. Платформа требует, чтобы чтение данных происходило на стороне сервера. Однако простая передача файла на сервер, его чтение в объект и возврат этого объекта на клиент приводят к потере метаданных о пути к файлу. В итоге мы получаем «безымянный» документ.
Разберем по шагам, как правильно организовать этот процесс, используя современные возможности платформы.
Этот способ является наиболее правильным с точки зрения архитектуры управляемого приложения. Мы используем асинхронный вызов для выбора и передачи файла на сервер, а затем возвращаем прочитанный документ, принудительно устанавливая связь с исходным путем.
Проанализируем следующий пример кода:
&НаКлиенте
Процедура ОткрытьФайл(Команда)
// Настраиваем параметры диалога выбора файла
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов("Открытие файла", Ложь, "Табличный документ(*.mxl)|*.mxl");
// Создаем описание оповещения для обработки результата
ОписаниеЗавершение = Новый ОписаниеОповещения("ЗавершениеПомещенияФайла", ЭтаФорма);
// Помещаем файл во временное хранилище
НачатьПомещениеФайлаНаСервер(ОписаниеЗавершение, , , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ЗавершениеПомещенияФайла(ОписаниеПомещенногоФайла, ДопПараметры) Экспорт
Если ОписаниеПомещенногоФайла = Неопределено Тогда
Возврат; // Пользователь отменил выбор
КонецЕсли;
Адрес = ОписаниеПомещенногоФайла.Адрес;
ПутьКФайлу = ОписаниеПомещенногоФайла.СсылкаНаФайл.Файл.ПолноеИмя;
// Создаем новый объект и читаем данные на сервере
ТабДок = Новый ТабличныйДокумент;
ПрочитатьТабДокНаСервере(ТабДок, Адрес);
// Ключевой момент: устанавливаем связь с файлом через метод Показать
// Второй параметр метода - это путь к файлу, который "привязывает" документ к диску
ТабДок.Показать(ПутьКФайлу, ПутьКФайлу);
КонецПроцедуры
&НаСервере
Процедура ПрочитатьТабДокНаСервере(ТабДок, Адрес)
// Получаем данные из хранилища и открываем поток для чтения
Данные = ПолучитьИзВременногоХранилища(Адрес);
Поток = Данные.ОткрытьПотокДляЧтения();
ТабДок.Прочитать(Поток);
Поток.Закрыть();
КонецПроцедуры
В данном примере мы используем метод Показать(). Обратите внимание на его параметры: Показать(Заголовок, ИмяФайла). Передавая во второй параметр полный путь к исходному файлу ПутьКФайлу, мы сообщаем платформе, что этот документ открыт именно из этого места. Теперь стандартные команды сохранения будут работать корректно.
Существует и другой подход, который полезен, если вы выводите табличный документ не в отдельное окно через Показать(), а отображаете его непосредственно в поле формы. У объекта ТабличныйДокумент есть программное свойство ИспользуемоеИмяФайла.
Рассмотрим, как это работает. После того как мы прочитали документ на сервере и вернули его на клиент, нам необходимо выполнить следующее действие:
&НаКлиенте
Процедура ПослеЗагрузкиНаКлиенте(ПолученныйТабДок, ИсходныйПуть)
// Присваиваем объекту путь к файлу
ПолученныйТабДок.ИспользуемоеИмяФайла = ИсходныйПуть;
// Теперь, если этот ТабДок отображен на форме в поле табличного документа,
// система будет знать, куда его сохранять по умолчанию.
ЭтаФорма.Элементы.ПолеТабличногоДокумента.Отобразить(ПолученныйТабДок);
КонецПроцедуры
Важный нюанс: свойство ИспользуемоеИмяФайла доступно и на сервере, но для корректной работы интерактивных команд сохранения в тонком клиенте его лучше актуализировать именно на стороне клиента перед началом работы пользователя с документом.
Выясним причину, почему в современных версиях 1С (начиная с 8.3.10) предпочтительнее использовать потоки, а не временные файлы. Традиционный метод записи ДвоичныхДанных во временный файл через ПолучитьИмяВременногоФайла() и последующее чтение объекта ТабличныйДокумент из этого файла создает лишнюю нагрузку на дисковую подсистему сервера и требует ручного удаления временного файла через УдалитьФайлы().
Посмотрим на более эффективный вариант реализации серверной части:
&НаСервере
Функция ПрочитатьMXL_в_ТабДок(Адрес)
ТабДок = Новый ТабличныйДокумент;
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(Адрес);
// Чтение напрямую из потока в памяти
Поток = ДвоичныеДанныеФайла.ОткрытьПотокДляЧтения();
Попытка
ТабДок.Прочитать(Поток, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
КонецПопытки;
Поток.Закрыть();
Возврат ТабДок;
КонецФункции
В методе Прочитать() мы указали параметр СпособЧтенияЗначенийТабличногоДокумента.Значение. Это позволяет сохранить типизацию данных в ячейках (даты как даты, числа как числа), что критически важно, если файл .mxl в дальнейшем будет использоваться для расчетов или обработки данных, а не просто для визуального просмотра.
Проанализируем ситуацию при работе через браузер. В веб-клиенте политика безопасности не позволяет платформе 1С свободно обращаться к файловой системе пользователя. Поэтому методы Показать() с указанием пути или установка свойства ИспользуемоеИмяФайла не приведут к тому, что браузер автоматически перезапишет файл при нажатии «Сохранить».
В веб-клиенте алгоритм всегда будет следующим:
Для бесшовной работы, максимально приближенной к работе в тонком клиенте, рекомендуется устанавливать Расширение работы с файлами, которое частично снимает эти ограничения в поддерживаемых браузерах.
Иногда разработчики пытаются вызвать стандартную команду Файл - Открыть программно. Однако платформа не предоставляет прямого метода для вызова этого диалога с возвратом управления в код. Поэтому описанная выше связка: ДиалогВыбораФайла -> Помещение во временное хранилище -> Прочитать() на сервере -> Показать() на клиенте — является единственным надежным способом имитации системного поведения.
Подведем итог. Чтобы успешно открыть mxl файл на клиенте и не потерять связь с диском, мы должны:
ТабличныйДокумент.Прочитать() на сервере, желательно используя потоки.Показать() с указанием пути или заполнить ИспользуемоеИмяФайла.Такой подход гарантирует стабильную работу и предсказуемое поведение интерфейса для конечного пользователя.