В процессе разработки часто возникает задача: прочитать данные из файла Excel, который находится на сервере или загружается пользователем, и предоставить пользователю возможность выбрать конкретный лист для обработки. В этой статье мы подробно разберем, как реализовать передачу списка листов с сервера на клиент, какие методы 1С для этого подходят лучше всего и как избежать типичных ошибок при работе с COM-объектами.
Представим ситуацию: на форме есть поле ввода с путем к файлу или кнопка загрузки. Нам необходимо при нажатии на кнопку выбора в поле «Лист» получить актуальный список имен листов из книги Excel. Основная сложность заключается в том, что файл может находиться в сетевом ресурсе, доступном только серверу 1С, а интерфейсное взаимодействие (выбор из списка) происходит на клиенте. Рассмотрим по шагам, как организовать это взаимодействие в управляемом приложении.
Проанализируем наиболее гибкий вариант, когда список листов запрашивается в момент обращения пользователя к элементу формы. Для этого мы будем использовать событие НачалоВыбора. Главный секрет здесь заключается в параметре СтандартнаяОбработка, который нужно установить в значение Ложь, чтобы подавить стандартное окно выбора и подставить свои данные.
Разберем пример кода для клиентской и серверной части:
&НаКлиенте
Процедура ВыберитеНомерЛистаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Останавливаем стандартный механизм выбора
СтандартнаяОбработка = Ложь;
// Вызываем серверную функцию для получения списка
// Предполагается, что путь к файлу хранится в реквизите 'ПутьКФайлу'
СписокЛистов = ПолучитьСписокЛистовНаСервере(Объект.ПутьКФайлу);
// Заполняем данные выбора, которые 1С подставит в выпадающий список
ДанныеВыбора = СписокЛистов;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьСписокЛистовНаСервере(Знач ПутьКФайлу)
Список = Новый СписокЗначений;
Попытка
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.WorkBooks.Open(ПутьКФайлу);
Для Каждого ТекущийЛист Из Книга.WorkSheets Цикл
Список.Добавить(ТекущийЛист.Name);
КонецЦикла;
Книга.Close(Ложь);
Excel.Quit();
Исключение
// Важно закрыть процесс Excel в случае ошибки
Если Excel <> Неопределено Тогда
Excel.Quit();
КонецЕсли;
Сообщить("Ошибка при чтении файла: " + ОписаниеОшибки());
КонецПопытки;
Возврат Список;
КонецФункции
В данном примере мы использовали директиву &НаСервереБезКонтекста. Это лучшая практика, так как такая функция работает быстрее, не передавая все данные формы на сервер и обратно. Мы передаем только путь к файлу и получаем готовый СписокЗначений.
Выясним причину, почему иногда удобнее использовать реквизит формы. Если список листов требуется нам не один раз, а постоянно (например, для проверки корректности данных), мы можем создать реквизит формы с типом СписокЗначений.
Рассмотрим алгоритм действий:
ДоступныеЛисты с типом СписокЗначений.СписокВыбора этот реквизит.Это позволит избежать повторных и тяжелых вызовов COM-объекта при каждом клике пользователя, так как данные уже будут находиться в контексте формы на стороне клиента.
Часто начинающие программисты используют жесткую привязку к номеру листа, например Лист = Книга.WorkSheets(2). Проанализируем ситуацию: если пользователь выберет в списке «Лист 3», а в коде прописано число 2, загрузка пройдет некорректно. Чтобы этого избежать, нужно передавать в процедуру обработки не индекс, а имя листа, полученное из списка выбора.
Посмотрим на пример правильной инициализации листа по его имени:
// В процедуре обработки на сервере
ЛистДляЗагрузки = Книга.WorkSheets(ВыбранноеИмяЛиста);
// Теперь объект 'ЛистДляЗагрузки' указывает именно на тот лист, который выбрал пользователь
Работа с COMОбъект("Excel.Application") на сервере сопряжена с рисками: процесс может «зависнуть», потребляя память, или Excel может быть вовсе не установлен на серверной ОС (особенно если это Linux). Рассмотрим более надежный и современный метод — использование встроенного объекта ТабличныйДокумент.
Начиная с версии 8.3.6, 1С умеет нативно читать файлы XLSX. Хотя ТабличныйДокумент не предоставляет прямой список имен листов так же просто, как COM, он работает значительно стабильнее. Если же вам критически важно получить именно структуру книги без установки Office, рекомендуем обратить внимание на ADO (ActiveX Data Objects) или чтение XLSX как ZIP-архива с последующим разбором workbook.xml.
Пример получения данных через ТабличныйДокумент (без COM):
&НаСервере
Процедура ПрочитатьФайлНативно(Путь)
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(Путь, СпособЧтенияТабличногоДокумента.XLSX);
// Далее работаем с ТабДок как с обычной таблицей 1С
Исключение
Сообщить("Файл не прочитан");
КонецПопытки;
КонецПроцедуры
При работе в клиент-серверном варианте всегда помните о следующих моментах:
USR1CV8). Убедитесь, что у этой учетной записи есть права доступа к папке, в которой лежит Excel-файл.ПоместитьФайлНаСервер, а затем на сервере работайте с временным файлом. Прямой путь типа C:\Users\Admin\Documents... сервер не увидит.Книга.Close() и Excel.Quit() в блоке Попытка...Исключение...КонецПопытки.Таким образом, мы разобрали основной паттерн взаимодействия: получение списка через серверную функцию-контейнер и передачу его в ДанныеВыбора на клиенте. Этот подход является стандартным для платформы 1С:Предприятие 8.3 и обеспечивает отзывчивость интерфейса при работе с внешними данными.