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

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

Переход с обычных форм на управляемые в 1С часто вызывает сложности у разработчиков, особенно когда речь идет о взаимодействии с файловой системой. Одной из классических задач является загрузка данных из Excel, где пользователю необходимо выбрать конкретный лист из многостраничного документа. В обычном приложении это решалось простым вызовом модального окна, но в управляемом приложении и при отказе от модальности нам требуется использовать асинхронные методы и работу с временным хранилищем. Рассмотрим, как правильно выстроить эту логику, сохранив производительность и удобство для пользователя.

Проблема асинхронности и потери данных

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

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

Шаг 1. Помещение файла во временное хранилище

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


&НаКлиенте
Процедура ВыполнитьЗагрузкуИзФормы(Команда)
    
    ПеременнаяАдресФайла = ""; 
    ОписаниеОповещения = Новый ОписаниеОповещения("ВыполнитьЗагрузкуЗавершение", ЭтотОбъект);
    
    // Важно передать УникальныйИдентификатор формы, чтобы файл жил до закрытия окна
    НачатьПомещениеФайла(ОписаниеОповещения, ПеременнаяАдресФайла, , Ложь, УникальныйИдентификатор);
    
КонецПроцедуры

Использование параметра УникальныйИдентификатор гарантирует, что файл не будет удален из памяти сервера сразу после завершения текущего вызова, а дождется окончания работы с формой.

Шаг 2. Получение списка листов и асинхронный выбор

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

Рассмотрим процедуру обработки завершения помещения файла:


&НаКлиенте
Процедура ВыполнитьЗагрузкуЗавершение(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
    
    Если Не Результат Тогда
        Возврат;
    КонецЕсли;

    // Сохраняем адрес в реквизит формы, чтобы он был доступен позже
    Объект.АдресВоВременномХранилище = Адрес;
    
    // Получаем список листов на сервере
    РезультатСервера = ПолучитьСписокЛистовНаСервере(Адрес);
    СписокЛистов = РезультатСервера.СписокЛистов;
    
    Если СписокЛистов.Количество() > 1 Тогда
        // Вместо модального окна используем ПоказатьВыборИзСписка
        Оповещение = Новый ОписаниеОповещения("ПослеВыбораЛиста", ЭтотОбъект);
        ПоказатьВыборИзСписка(Оповещение, СписокЛистов);
    ИначеЕсли СписокЛистов.Количество() = 1 Тогда
        ВыполнитьЗагрузкуНаСервере(СписокЛистов[0].Значение);
    КонецЕсли;
    
КонецПроцедуры

Шаг 3. Оптимизация работы с Табличным Документом

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


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

Важный момент: В современных версиях платформы 1С (начиная с 8.3.10) для работы с Excel рекомендуется использовать методы, не требующие наличия установленного Microsoft Excel на сервере. Чтение в ТабличныйДокумент отлично справляется с этой задачей даже на Linux-серверах.

Шаг 4. Завершение загрузки выбранного листа

Когда пользователь выбрал лист, мы вызываем финальную серверную процедуру. Теперь нам не нужно открывать файл — мы просто берем готовый объект из памяти.


&НаКлиенте
Процедура ПослеВыбораЛиста(ЭлементСписка, Параметры) Экспорт
    
    Если ЭлементСписка <> Неопределено Тогда
        ВыполнитьЗагрузкуНаСервере(ЭлементСписка.Значение);
    КонецЕсли;
    
КонецПроцедуры

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

Рекомендации по улучшению

  1. Использование БСП: Если в вашей конфигурации подключена Библиотека Стандартных Подсистем, рассмотрите возможность использования функции ФайловаяСистемаКлиент.ПоместитьФайл. Она берет на себя часть рутины по созданию описаний оповещений.
  2. Очистка ресурсов: Несмотря на то что УникальныйИдентификатор автоматически очистит хранилище при закрытии формы, при работе с очень большими файлами (100+ Мб) хорошим тоном считается принудительный вызов УдалитьИзВременногоХранилища сразу после окончания обработки данных.
  3. Проверка форматов: При получении списка листов через ТабДок.Области, помните, что в некоторых версиях Excel или при специфичном сохранении именованные области могут не соответствовать листам. Альтернативный вариант — использование внешних компонент или чтение через COMОбъект (только для Windows-серверов с установленным Excel).
  4. Пользовательский интерфейс: Для выбора листа вместо ПоказатьВопрос лучше использовать ПоказатьВыборИзСписка. Это создает более удобное выпадающее меню прямо рядом с кнопкой загрузки, что выглядит современно и лаконично.

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

← На главную