Часто при разработке механизмов интеграции и загрузки данных программисты 1С сталкиваются с необходимостью чтения файлов Excel. Классическим способом прошлых лет было использование технологии ADODB (ActiveX Data Objects Database). Однако, этот метод таит в себе множество подводных камней.
Разберем типичную ситуацию: при попытке узнать количество строк в считанном листе возникает ошибка: "Произошла исключительная ситуация (ADODB.Recordset): Операция не допускается, если объект закрыт.". Кроме того, данная ошибка может быть «плавающей»: у одних пользователей она появляется, у других нет. Выясним причину этой проблемы и рассмотрим современные альтернативы.
Посмотрим на фрагмент кода, в котором разработчик осуществляет чтение данных листа в объект ADODB.Recordset и проверяет его на заполненность:
Попытка
ADODBRecordset = Новый COMОбъект("ADODB.Recordset");
ADODBRecordset.Open(ТекстЗапроса, ADODBConnection);
// Проверка заполненности листа.
Если (ADODBRecordset.EOF ИЛИ ADODBRecordset.BOF) Тогда
КолвоСтрокExcel = 0;
Сообщить(НСтр("ru = '" + ИмяЛиста + ": не содержит данных.'"), СтатусСообщения.Внимание);
// Закрытие Объектов.
ADODBRecordset.Close();
ADODBConnection.Close();
ADODBRecordset = Неопределено;
ADODBConnection = Неопределено;
//Возврат Новый ТаблицаЗначений; // ЗАКОММЕНТИРОВАНО!
КонецЕсли;
// ...
Исключение
// ...
КонецПопытки;
// Ошибка возникает на следующей строке:
КолвоСтрокExcel = ADODBRecordset.RecordCount + 1;
Проанализируем ситуацию по шагам. Ошибка кроется в банальной логике обработки условий:
ADODBRecordset.EOF ИЛИ ADODBRecordset.BOF.Close() для рекордсета и коннекшена, а переменным присваивается Неопределено.Возврат был закомментирован разработчиком.КолвоСтрокExcel = ADODBRecordset.RecordCount + 1;, программа обращается к объекту ADODBRecordset, который мы только что принудительно закрыли и уничтожили. Платформа закономерно выбрасывает исключение: "Операция не допускается, если объект закрыт".Чтобы починить именно этот участок кода, достаточно раскомментировать инструкцию Возврат, чтобы после закрытия объектов программа корректно выходила из функции.
Даже если исправить логическую ошибку, чтение файлов `.xls` и `.xlsx` через ADODB — процесс крайне капризный. Рассмотрим подробнее, почему от этого подхода в мире 1С сейчас активно отказываются:
Microsoft.Jet.OLEDB.4.0 (для старых файлов xls) существует только в 32-битной версии. Если сервер 1С запущен в 64-битной среде, подключиться через него не выйдет. Драйвер Microsoft.ACE.OLEDB.12.0 имеет 64-битную версию, но она конфликтует с установленным 32-битным MS Office.IMEX=1, чтобы весь смешанный контент колонки считывался как текст. Однако провайдер по умолчанию анализирует только первые 8 строк таблицы (параметр реестра TypeGuessRows). Если первые 8 строк содержат числа, колонка получит числовой тип, и все строковые значения ниже 8-й строки превратятся в Null.SELECT * FROM [ИмяЛиста$] может вернуть ошибку. Требуется строгая нормализация имен.Если мы решим уйти от ADODB и попытаемся использовать родной объект 1С ТабличныйДокумент, мы можем столкнуться с новой преградой: "Ошибка при выполнении файловой операции" при вызове метода Прочитать(). Выясним причину.
Когда код исполняется на сервере 1С (в серверном контексте), метод пытается найти файл по указанному пути именно на дисках сервера, а не на компьютере пользователя (клиента).
Если файл открыт в Excel на клиенте или серверная служба 1С (запущенная под локальным системным пользователем USR1CV8) не имеет прав доступа к указанной сетевой директории, мы получим ошибку файловой операции.
Как правильно передать файл с клиента на сервер:
ПоместитьВоВременноеХранилище()).ПолучитьИзВременногоХранилища()).ПолучитьИмяВременногоФайла()) и уже этот локальный серверный файл считывать в системе.Вместо использования устаревших COM-объектов ADODB, платформа 1С:Предприятие 8 позволяет реализовать загрузку данных из Excel невероятно компактно и быстро. Рассмотрим изящный метод, укладывающийся всего в 8 строк кода. Данный способ использует связку ТабличныйДокумент и ПостроительЗапроса.
Посмотрим на пример кода:
// 1. Создаем пустой табличный документ
ТабДок = Новый ТабличныйДокумент;
// 2. Читаем Excel-файл (поддерживаются и xls, и xlsx) напрямую в документ
ТабДок.Прочитать(ПолноеИмяФайлаНаСервере, СпособЧтенияЗначенийТабличногоДокумента.Значение);
// 3. Создаем ПостроительЗапроса
ПЗ = Новый ПостроительЗапроса;
// 4. Передаем заполненную область табличного документа как источник данных
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
// 5. Отключаем автоматическое добавление представлений для скорости
ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
// 6. Инициализируем настройки
ПЗ.ЗаполнитьНастройки();
// 7. Выполняем псевдо-запрос
ПЗ.Выполнить();
// 8. Выгружаем результат в готовую Таблицу Значений
ТаблицаРезультат = ПЗ.Результат.Выгрузить();
Разберем по шагам, почему этот способ лучше:
ТабДок.Прочитать() работает нативно, не требуя установки драйверов Office (ACE OLEDB) ни на клиенте, ни на сервере.ОписаниеИсточникаДанных. Платформа 1С самостоятельно разбирает колонки и формирует структуру.Выгрузить() формирует готовую ТаблицаЗначений, где имена колонок автоматически берутся из первой строки таблицы Excel (если они удовлетворяют требованиям именования переменных в 1С). Далее мы можем обходить эту таблицу обычным циклом Для Каждого СтрокаТЗ Из ТаблицаРезультат Цикл.Используйте этот современный подход для небольших и средних по объему файлов Excel. Это убережет вас от плавающих ошибок COM-соединений, проблем с разрядностью серверов и сэкономит десятки строк трудночитаемого кода.