При автоматизации обмена данными часто возникает задача регулярной загрузки информации из нескольких файлов Excel, расположенных в определенном каталоге. Когда обработка запускается вручную пользователем, процесс проходит без сбоев. Однако при переносе кода в фоновое или регламентное задание на стороне сервера разработчики сталкиваются с рядом специфических ошибок: задание завершается без видимого результата, зависают процессы или COM-объект вовсе отказывается инициализироваться.
Разберем подробно, как правильно организовать пакетный обход файлов в каталоге, как корректно работать с объектами COM, почему регламентные задания на сервере не могут прочитать файлы и какие существуют более надежные альтернативы.
Для того чтобы не привязываться к жесткому количеству файлов или их фиксированным именам, проанализируем использование системной функции платформы НайтиФайлы(). С ее помощью мы получаем массив объектов типа Файл, содержащих полную информацию о каждом найденном документе.
Рассмотрим типовой шаблон поиска файлов с расширениями .xls и .xlsx:
КаталогОбмена = "\\it-file\Обмен\ФайлыПроверки";
// Маска *.xls* позволяет захватить как старый формат .xls, так и современный .xlsx
МаскаПоиска = "*.xls*";
НайденныеФайлы = НайтиФайлы(КаталогОбмена, МаскаПоиска);
Если НайденныеФайлы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Если используется автоматизация через COM-соединение, критически важно выносить инициализацию и закрытие приложения Excel.Application за пределы цикла обработки файлов. Если создавать и уничтожать COM-объект на каждой итерации, в операционной системе могут зависать фоновые процессы EXCEL.EXE, вызывая утечки оперативной памяти.
Посмотрим на правильную структуру пакетного чтения и создания документов в 1С:
КаталогОбмена = "\\it-file\Обмен\ФайлыПроверки";
НайденныеФайлы = НайтиФайлы(КаталогОбмена, "*.xls*");
Если НайденныеФайлы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Попытка
Excel = Новый COMОбъект("Excel.Application");
Excel.DisplayAlerts = Ложь;
Excel.Visible = Ложь;
Исключение
ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Возврат;
КонецПопытки;
Для Каждого ТекФайл Из НайденныеФайлы Цикл
Книга = Неопределено;
Попытка
Книга = Excel.WorkBooks.Open(ТекФайл.ПолноеИмя);
Исключение
ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Предупреждение,,, "Не удалось открыть файл: " + ТекФайл.ПолноеИмя + ". Ошибка: " + ОписаниеОшибки());
Продолжить;
КонецПопытки;
Попытка
Лист = Книга.Sheets(1);
// Определение количества заполненных строк
ФайлСтрок = Лист.Cells(1, 1).SpecialCells(11).Row;
НовыйДокумент = Документы.СуточныеПоказателиСкважин.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.УстановитьНовыйНомер();
Для НомерСтроки = 2 По ФайлСтрок Цикл
Водозабор = Лист.Cells(НомерСтроки, 1).Text;
ДатаДо = Лист.Cells(НомерСтроки, 2).Text;
Скважина = Лист.Cells(НомерСтроки, 3).Text;
Водомер = Лист.Cells(НомерСтроки, 4).Text;
Сутки = Лист.Cells(НомерСтроки, 5).Text;
Час = Лист.Cells(НомерСтроки, 6).Text;
Ток = Лист.Cells(НомерСтроки, 7).Text;
Давление = Лист.Cells(НомерСтроки, 8).Text;
// Заполнение реквизитов и табличной части документа
КонецЦикла;
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
// Закрываем книгу без сохранения изменений
Книга.Close(Ложь);
// Переносим или удаляем успешно обработанный файл
УдалитьФайлы(ТекФайл.ПолноеИмя);
Исключение
Если Книга <> Неопределено Тогда
Книга.Close(Ложь);
КонецЕсли;
ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Ошибка,,, "Ошибка обработки файла: " + ТекФайл.ПолноеИмя + ". Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
// Полное освобождение ресурсов COM
Excel.Application.Quit();
Excel = Неопределено;
Выясним главные причины, по которым код, успешно работающий в интерактивном режиме на клиенте, дает сбой при запуске в регламентном задании под сервером 1С:
Сервер и Вызов сервера (при необходимости). Если процедура объявлена только для контекста Клиент (обычное приложение), серверное регламентное задание к ней обратиться не сможет.Исключение: если при возникновении сбоя в коде прописан пустой оператор Продолжить или Возврат, консоль заданий покажет успешное завершение, хотя фактически ни один файл не был прочитан. Любые ошибки необходимо фиксировать через ЗаписьЖурналаРегистрации().1C:Enterprise Server часто запускается под системным пользователем USR1CV8 или SYSTEM. У этой учетной записи по умолчанию нет сетевых прав на сетевые каталоги (пути вида \\server\share).Workbooks.Open() аварийно прекращает работу. Для решения проблемы на сервере создаются каталоги:
C:\Windows\System32\config\systemprofile\Desktop
C:\Windows\SysWOW64\config\systemprofile\Desktop (для 64-битных ОС).dcomcnfg.Использование COM на сервере официально не поддерживается самой компанией Microsoft из-за нестабильности и утечек памяти. Начиная с платформы 8.3, 1С умеет читать файлы .xlsx и .xls нативно без использования сторонних программ и COM-компонентов с помощью метода Прочитать() объекта ТабличныйДокумент.
Рассмотрим, как переписать процедуру чтения на быстрый и безопасный для сервера способ:
КаталогОбмена = "\\it-file\Обмен\ФайлыПроверки";
НайденныеФайлы = НайтиФайлы(КаталогОбмена, "*.xls*");
Для Каждого ТекФайл Из НайденныеФайлы Цикл
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ТекФайл.ПолноеИмя);
Исключение
ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось прочитать файл табличным документом: " + ТекФайл.ПолноеИмя + ". " + ОписаниеОшибки());
Продолжить;
КонецПопытки;
ВсегоСтрок = ТабДок.ВысотаТаблицы;
Если ВсегоСтрок < 2 Тогда
Продолжить; // Файл пустой или содержит только заголовок
КонецЕсли;
НовыйДокумент = Документы.СуточныеПоказателиСкважин.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.УстановитьНовыйНомер();
Для НомерСтроки = 2 По ВсегоСтрок Цикл
Водозабор = ТабДок.Область(НомерСтроки, 1).Текст;
ДатаДо = ТабДок.Область(НомерСтроки, 2).Текст;
Скважина = ТабДок.Область(НомерСтроки, 3).Текст;
Водомер = ТабДок.Область(НомерСтроки, 4).Текст;
Сутки = ТабДок.Область(НомерСтроки, 5).Текст;
Час = ТабДок.Область(НомерСтроки, 6).Текст;
Ток = ТабДок.Область(НомерСтроки, 7).Текст;
Давление = ТабДок.Область(НомерСтроки, 8).Текст;
// Обработка и сохранение реквизитов документа
КонецЦикла;
Попытка
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
УдалитьФайлы(ТекФайл.ПолноеИмя);
Исключение
ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Ошибка,,,
"Ошибка проведения документа по файлу: " + ТекФайл.ПолноеИмя + ". " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Преимущества метода ТабличныйДокумент.Прочитать():