Как передать список листов Excel с сервера на клиент и заполнить поле выбора в 1С?

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

При разработке обработок по импорту данных из внешних файлов (в частности, Microsoft Excel) программисты часто сталкиваются с необходимостью предоставить пользователю выбор конкретного листа для загрузки. Поскольку само чтение файла и работа с COM-объектами происходят в безопасном и производительном контексте сервера, а выбор листа пользователем — исключительно на клиенте, возникает классическая задача 1С: передача данных через клиент-серверную границу.

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

В чем заключается основная сложность?

Попытка напрямую обратиться к серверным переменным или передать тяжелые COM-объекты (например, Excel.Application) на клиентскую сторону вызовет ошибку платформы. На клиенте недоступны типы данных, специфичные для сервера (например, ТаблицаЗначений или ссылки на COM-серверы). Поэтому передавать нужно простые, сериализуемые типы данных — массивы, структуры или заполнять элементы формы непосредственно на сервере, используя контекст формы.

---

Решение 1. Прямое заполнение списка выбора элемента формы на сервере

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

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

  1. Пользователь выбирает файл на клиенте (через диалог выбора файла).
  2. Путь к файлу записывается в реквизит формы.
  3. Вызывается серверная процедура, которая открывает Excel, считывает имена листов и заполняет список выбора.

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

Шаг 1. Клиентская процедура выбора файла

В обработчике начала выбора для поля ввода пути к файлу мы открываем стандартный диалог. После успешного выбора файла мы сразу же запускаем процедуру получения списка листов.


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

Шаг 2. Серверная процедура получения листов

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


&НаСервере
Процедура ЗаполнитьСписокЛистовНаСервере()
    
    // Проверяем, указан ли файл
    Если Не ЗначениеЗаполнено(Объект.ПутьКФайлу) Тогда
        Возврат;
    КонецЕсли;
    
    Попытка
        Эксель = Новый COMОбъект("Excel.Application");
    Исключение
        ВызватьИсключение "Не удалось запустить MS Excel. Убедитесь, что приложение установлено на сервере.";
    КонецПопытки;
    
    Попытка
        // Открываем книгу в невидимом режиме для ускорения работы
        Эксель.Visible = Ложь;
        Эксель.DisplayAlerts = Ложь;
        
        Книга = Эксель.Workbooks.Open(Объект.ПутьКФайлу);
        
        // Получаем доступ к списку выбора элемента формы
        СписокЛистовВыбора = Элементы.ВыберитеНомерЛиста.СписокВыбора;
        СписокЛистовВыбора.Очистить(); // Очищаем старые значения!
        
        // Обходим все листы книги и добавляем их имена
        Для Каждого ТекущийЛист Из Книга.WorkSheets Цикл
            СписокЛистовВыбора.Добавить(ТекущийЛист.Name);
        КонецЦикла;
        
        // Корректно закрываем файл и завершаем процесс Excel
        Книга.Close(Ложь);
        Эксель.Quit();
    Исключение
        // В случае ошибки обязательно закрываем Excel, чтобы процесс не "завис" в памяти
        Если ПреобразоватьКЧислу(Эксель) > 0 Тогда
            Эксель.Quit();
        КонецЕсли;
        ЗаписатьЛогСобытия("Ошибка при чтении листов Excel: " + ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
        ВызватьИсключение "Ошибка при чтении структуры файла Excel. Подробности в журнале регистрации.";
    КонецПопытки;
    
КонецПроцедуры

---

Решение 2. Передача таблицы значений (или списка) через временное хранилище

Что делать, если список листов или прочитанные данные таблицы Excel нужно не просто вывести в поле выбора, а сохранить на клиенте для дальнейшей сложной логики (например, для интерактивного сопоставления колонок)? В таком случае напрямую работать с элементами формы неудобно. Лучше использовать универсальный механизм 1С — Временное хранилище.

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

Рассмотрим алгоритм реализации:


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

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

---

Решение 3. Преобразование Таблицы Значений в массив структур

В практической разработке часто требуется передать полноценную ТаблицуЗначений (ТЗ) с сервера на клиент. Напрямую передать объект типа ТаблицаЗначений на клиент нельзя — платформа выдаст ошибку несоответствия типов. Отличным решением является конвертация таблицы в массив структур. Структуры и массивы прекрасно чувствуют себя на клиенте и легко поддаются обходу в циклах.

Проанализируем вспомогательную функцию, которая быстро решает эту задачу:


// Преобразует таблицу значений в массив структур для передачи на клиент.
// Параметры:
//  ТаблицаЗначений - ТаблицаЗначений - исходные данные.
// Возвращаемое значение:
//  Массив - массив структур, повторяющих строки исходной таблицы.
Функция ТаблицаЗначенийВМассив(ТаблицаЗначений) Экспорт
    
    МассивРезультата = Новый Массив;
    СтрокаКолонок = "";
     НужнаЗапятая = Ложь;
    
    // Формируем строку колонок для инициализации структур
    Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
        Если НужнаЗапятая Тогда
            СтрокаКолонок = СтрокаКолонок + ",";
        КонецЕсли;
        СтрокаКолонок = СтрокаКолонок + Колонка.Имя;
        НужнаЗапятая = Истина;
    КонецЦикла;
    
    // Заполняем массив структур данными
    Для Каждого СтрокаТЗ Из ТаблицаЗначений Цикл
        НоваяСтруктура = Новый Структура(СтрокаКолонок);
        ЗаполнитьЗначенияСвойств(НоваяСтруктура, СтрокаТЗ);
        МассивРезультата.Добавить(НоваяСтруктура);
    КонецЦикла;
    
    Возврат МассивРезультата;
    
КонецФункции

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

---

Современная оптимизация: отказываемся от медленного COM-соединения

Использование COMОбъект("Excel.Application") имеет ряд критических недостатков:

Начиная с современных версий платформы 1С:Предприятие 8.3, разработчикам доступен быстрый нативный механизм чтения файлов Excel без использования COM. Мы можем прочитать файл напрямую в ТабличныйДокумент.

Проанализируем пример кода чтения структуры листов с помощью нативных механизмов 1С:


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

Этот подход полностью кроссплатформенный, работает в разы быстрее и не требует лицензий на сторонний офисный софт на стороне сервера.

---

Резюме и рекомендации

Для успешного обмена данными между сервером и клиентом при работе с внешними файлами придерживайтесь простых правил:

  1. Разделяйте ответственность: интерактивные действия (выбор файла, нажатие кнопок) делайте на клиенте, тяжелую работу с файлами и базами данных — на сервере.
  2. Очищайте коллекции: перед каждым заполнением списков выбора или таблиц на форме всегда вызывайте метод Очистить(), чтобы избежать размножения дубликатов при повторных действиях пользователя.
  3. Закрывайте ресурсы: при работе с COM-объектами всегда оборачивайте код в блок Попытка...Исключение и гарантированно закрывайте процессы Excel (метод Quit()) в блоке исключения, чтобы не перегружать память сервера.
← На главную