Чтение файлов Excel — одна из самых частых задач в практике разработчика 1С. Традиционный метод с использованием COM-соединения Excel.Application постепенно уходит в прошлое. Он медленный, требует обязательной установки Microsoft Office на компьютере или сервере, жестко привязан к операционной системе Windows и часто приводит к зависанию процессов в оперативной памяти.
В этой статье мы подробно разберем, как правильно реализовать чтение файлов Excel в штатный ТабличныйДокумент в управляемом приложении 1С, избежав типичных ошибок с правами доступа и файловыми операциями на сервере. Также мы рассмотрим альтернативные и очень быстрые способы разбора полученных данных.
Прежде чем перейти к коду, давайте проанализируем важное ограничение платформы 1С:Предприятие. Метод ТабличныйДокумент.Прочитать() недоступен в Тонком и Веб-клиентах. Разработчики платформы сделали это ограничение намеренно, так как обработка тяжелых бинарных файлов требует значительных ресурсов, которые должны выделяться на стороне сервера.
Попытка прочитать файл на клиенте без обращения к серверу штатными методами платформы невозможна. Единственный правильный архитектурный путь в управляемом приложении выглядит следующим образом:
ТабличныйДокумент.Разберем этот процесс по шагам и выясним, почему часто возникает популярная ошибка «Ошибка при выполнении файловой операции».
Для безопасной передачи файла на сервер мы будем использовать асинхронный метод НачатьПомещениеФайлаНаСервер() (или его аналог НачатьПомещениеФайла() в более ранних версиях платформы). Это позволяет избежать зависания интерфейса у пользователя при загрузке больших файлов.
Рассмотрим пример клиентского кода:
&НаКлиенте
Процедура ВыбратьИЗагрузитьФайлExcel(Команда)
// Настраиваем диалог выбора файла
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Файлы Excel (*.xls;*.xlsx)|*.xls;*.xlsx";
Диалог.МножественныйВыбор = Ложь;
Диалог.Заголовок = "Выберите файл Excel для импорта";
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
Диалог.Показать(ОписаниеОповещения);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораФайла(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Если ВыбранныеФайлы = Неопределено Или ВыбранныеФайлы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ПутьКФайлу = ВыбранныеФайлы[0];
ФайлИнфо = Новый Файл(ПутьКФайлу);
РасширениеФайла = ФайлИнфо.Расширение; // Сохраняем расширение (например, ".xlsx")
// Подготавливаем параметры для помещения файла в хранилище
ОповещениеОкончания = Новый ОписаниеОповещения("ИмпортФайлаЗавершен", ЭтотОбъект, РасширениеФайла);
НачатьПомещениеФайлаНаСервер(ОповещениеОкончания, , , , ПутьКФайлу, УникальныйИдентификатор);
КонецПроцедуры
Когда файл успешно помещен во временное хранилище, управление передается в процедуру обратного вызова. Теперь нам нужно передать адрес хранилища на сервер, извлечь двоичные данные и прочитать их в табличный документ.
Важный нюанс: Многие разработчики пытаются использовать метод КаталогВременныхФайлов() для создания пути к файлу на сервере вручную (например, складывая путь и фиксированную строку "temp.xlsx"). Это грубая ошибка, которая приводит к двум проблемам:
usr1cv8), может не быть прав на запись в общие системные папки компьютера.temp.xlsx, что вызовет ошибку блокировки файла другим процессом.Для решения этой проблемы мы должны использовать метод ПолучитьИмяВременногоФайла(). Он генерирует абсолютно уникальное имя файла в специальной изолированной временной папке сервера 1С, к которой у процесса rphost гарантированно есть полные права доступа.
Также критически важно передать в метод ПолучитьИмяВременногоФайла() исходное расширение файла. Платформа 1С определяет алгоритм разбора файла (старый бинарный формат BIFF8 для .xls или современный XML-пакет для .xlsx) исключительно по его расширению. Если передать файл с расширением по умолчанию (.tmp), метод ТабДок.Прочитать() выдаст ошибку файловой операции.
Посмотрим на реализацию серверной части:
&НаКлиенте
Процедура ИмпортФайлаЗавершен(РезультатПомещения, ДополнительныеПараметры) Экспорт
Если РезультатПомещения = Неопределено Тогда
Возврат;
КонецЕсли;
РасширениеФайла = ДополнительныеПараметры; // Получаем расширение из параметров оповещения
АдресХранилища = РезультатПомещения.Адрес;
ТабличныйДок = Новый ТабличныйДокумент;
ПрочитатьФайлНаСервере(АдресХранилища, РасширениеФайла, ТабличныйДок);
// Выводим результат на форму (если на форме есть реквизит типа ТабличныйДокумент)
ЭтотОбъект.ТабличныйДокументНаФорме = ТабличныйДок;
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПрочитатьФайлНаСервере(Знач АдресХранилища, Знач РасширениеФайла, ТабличныйДок)
// Извлекаем двоичные данные из временного хранилища
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
// Генерируем уникальное имя файла с правильным расширением на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(РасширениеФайла);
// Записываем данные на диск сервера
ДвоичныеДанные.Записать(ИмяВременногоФайла);
Попытка
// Читаем данные в табличный документ
ТабличныйДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
ВызватьИсключение "Не удалось прочитать файл Excel. Возможно, повреждена структура файла. Описание: " + ОписаниеОшибки();
КонецПопытки;
// Обязательно удаляем временный файл с диска после чтения в память
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Логируем ошибку удаления, если файл остался заблокирован
КонецПопытки;
КонецПроцедуры
Когда мы читаем многостраничный документ Excel через метод ТабличныйДокумент.Прочитать(), платформа 1С загружает все листы книги в один документ. При этом каждый лист помещается в отдельную именованную область.
Мы можем легко управлять этим процессом и обращаться к конкретным страницам книги. Проанализируем, как получить список всех загруженных листов и работать с ними по отдельности:
&НаСервереБезКонтекста
Процедура РазборЛистовКниги(ТабличныйДок)
// Каждому листу Excel соответствует именованная область с аналогичным именем
Для Каждого Область Из ТабличныйДок.Области Цикл
ИмяЛиста = Область.Имя;
Сообщить("Обнаружен лист Excel с именем: " + ИмяЛиста);
// Мы можем получить область конкретного листа для дальнейшей обработки
ОбластьЛиста = ТабличныйДок.Область(ИмяЛиста);
// Например, узнаем высоту (количество строк) конкретного листа
КоличествоСтрок = ОбластьЛиста.ВысотаТаблицы;
Сообщить("Количество строк на листе: " + КоличествоСтрок);
КонецЦикла;
КонецПроцедуры
Зачастую нам нужно не просто показать Excel на форме, а быстро обработать его данные: перебрать строки, загрузить их в справочник или документ. Перебор ячеек табличного документа в цикле Для Строка = 1 По ВысотаТаблицы... — процесс медленный.
Существует изящный способ мгновенной конвертации табличного документа (или его отдельного листа) в стандартную ТаблицуЗначений с помощью объекта ПостроительЗапроса. Этот метод работает в десятки раз быстрее ручного обхода ячеек.
Рассмотрим, как это реализовать на практике:
&НаСервереБезКонтекста
Функция ПреобразоватьЛистВТаблицуЗначений(ТабличныйДок, ИмяЛиста)
// Выделяем область нужного нам листа
ОбластьЛиста = ТабличныйДок.Область(ИмяЛиста);
// Инициализируем построитель запроса
Построитель = Новый ПостроительЗапроса;
// Назначаем источником данных область табличного документа
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьЛиста);
// Выполняем построение и выгружаем результат в Таблицу Значений
Построитель.Выполнить();
ТаблицаДанных = Построитель.Результат.Выгрузить();
Возврат ТаблицаДанных;
КонецФункции
Важные требования при работе с этим лайфхаком:
ПостроительЗапроса автоматически преобразует эти тексты в имена колонок полученной таблицы значений._1С_Код.Дата), это придется сделать кодом при последующем обходе полученной таблицы значений.Если вам перед разработкой поставлена жесткая задача — «вообще не использовать серверные вызовы» (например, в распределенных мобильных клиентах с низкой пропускной способностью сети), то для формата .xlsx существует обходной путь.
Формат .xlsx — это обычный ZIP-архив, содержащий XML-файлы. Вы можете использовать объект ЧтениеZipФайла прямо на клиенте, распаковать архив во временный каталог и прочитать файлы разметки с помощью объекта ЧтениеXML:
xl/sharedStrings.xml хранит все текстовые строки книги.xl/worksheets/ (например, sheet1.xml) хранят индексы ячеек и ссылки на строки из реестра общих строк.Этот метод чрезвычайно быстрый и полностью кроссплатформенный (работает в веб-клиенте на любой ОС), но требует написания сложного парсера XML-структуры Excel «вручную» и не поддерживает старый формат .xls.
Мы выяснили, что оптимальный, стабильный и безопасный способ чтения Excel в 1С — это использование встроенного метода Прочитать() объекта ТабличныйДокумент на стороне сервера. Главное — правильно организовывать передачу файлов через временное хранилище, использовать ПолучитьИмяВременногоФайла() с передачей верного расширения (xls/xlsx) и не бояться применять ПостроительЗапроса для мгновенного получения готовой таблицы значений.