Как загрузить данные из нескольких файлов Excel в цикле регламентным заданием 1С

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

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

Разберем подробно, как правильно организовать пакетный обход файлов в каталоге, как корректно работать с объектами COM, почему регламентные задания на сервере не могут прочитать файлы и какие существуют более надежные альтернативы.

Организация поиска и обхода файлов в каталоге

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

Рассмотрим типовой шаблон поиска файлов с расширениями .xls и .xlsx:


КаталогОбмена = "\\it-file\Обмен\ФайлыПроверки";
// Маска *.xls* позволяет захватить как старый формат .xls, так и современный .xlsx
МаскаПоиска = "*.xls*";

НайденныеФайлы = НайтиФайлы(КаталогОбмена, МаскаПоиска);

Если НайденныеФайлы.Количество() = 0 Тогда
    Возврат;
КонецЕсли;

Чтение данных через COM-объект Excel.Application

Если используется автоматизация через 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 = Неопределено;

Почему регламентное задание на сервере не загружает Excel

Выясним главные причины, по которым код, успешно работающий в интерактивном режиме на клиенте, дает сбой при запуске в регламентном задании под сервером 1С:

  1. Неправильные флаги общего модуля: фоновые и регламентные задания выполняются строго в контексте сервера. В свойствах общего модуля обязательно должны стоять флаги Сервер и Вызов сервера (при необходимости). Если процедура объявлена только для контекста Клиент (обычное приложение), серверное регламентное задание к ней обратиться не сможет.
  2. Заглушение ошибок пустыми блоками Исключение: если при возникновении сбоя в коде прописан пустой оператор Продолжить или Возврат, консоль заданий покажет успешное завершение, хотя фактически ни один файл не был прочитан. Любые ошибки необходимо фиксировать через ЗаписьЖурналаРегистрации().
  3. Права системной учетной записи: служба 1C:Enterprise Server часто запускается под системным пользователем USR1CV8 или SYSTEM. У этой учетной записи по умолчанию нет сетевых прав на сетевые каталоги (пути вида \\server\share).
  4. Отсутствие системных папок Desktop: Microsoft Excel требует наличия рабочего стола даже в фоновом режиме. Если каталоги отсутствуют, метод Workbooks.Open() аварийно прекращает работу. Для решения проблемы на сервере создаются каталоги:
    C:\Windows\System32\config\systemprofile\Desktop
    C:\Windows\SysWOW64\config\systemprofile\Desktop (для 64-битных ОС).
  5. Несовпадение разрядности 32/64 bit: 64-разрядный сервер 1С не сможет напрямую инициализировать 32-разрядный COM-объект Excel без предварительной настройки DCOM через утилиту dcomcnfg.

Оптимальное решение: чтение Excel через встроенный ТабличныйДокумент

Использование COM на сервере официально не поддерживается самой компанией Microsoft из-за нестабильности и утечек памяти. Начиная с платформы 8.3, 1С умеет читать файлы .xlsx и .xls нативно без использования сторонних программ и COM-компонентов с помощью метода Прочитать() объекта ТабличныйДокумент.

Рассмотрим, как переписать процедуру чтения на быстрый и безопасный для сервера способ:


КаталогОбмена = "\\it-file\Обмен\ФайлыПроверки";
НайденныеФайлы = НайтиФайлы(КаталогОбмена, "*.xls*");

Для Каждого ТекФайл Из НайденныеФайлы Цикл
    
    ТабДок = Новый ТабличныйДокумент;
    
    Попытка
        ТабДок.Прочитать(ТекФайл.ПолноеИмя);
    Исключение
        ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Ошибка,,, 
            "Не удалось прочитать файл табличным документом: " + ТекФайл.ПолноеИмя + ". " + ОписаниеОшибки());
        Продолжить;
    КонецПопытки;
    
    ВсегоСтрок = ТабДок.ВысотаТаблицы;
    Если ВсегоСтрок < 2 Тогда
        Продолжить; // Файл пустой или содержит только заголовок
    КонецЕсли;
    
    НовыйДокумент = Документы.СуточныеПоказателиСкважин.СоздатьДокумент();
    НовыйДокумент.Дата = ТекущаяДата();
    НовыйДокумент.УстановитьНовыйНомер();
    
    Для НомерСтроки = 2 По ВсегоСтрок Цикл
        Водозабор = ТабДок.Область(НомерСтроки, 1).Текст;
        ДатаДо    = ТабДок.Область(НомерСтроки, 2).Текст;
        Скважина  = ТабДок.Область(НомерСтроки, 3).Текст;
        Водомер   = ТабДок.Область(НомерСтроки, 4).Текст;
        Сутки     = ТабДок.Область(НомерСтроки, 5).Текст;
        Час       = ТабДок.Область(НомерСтроки, 6).Текст;
        Ток       = ТабДок.Область(НомерСтроки, 7).Текст;
        Давление  = ТабДок.Область(НомерСтроки, 8).Текст;
        
        // Обработка и сохранение реквизитов документа
    КонецЦикла;
    
    Попытка
        НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
        УдалитьФайлы(ТекФайл.ПолноеИмя);
    Исключение
        ЗаписьЖурналаРегистрации("ЗагрузкаExcel", УровеньЖурналаРегистрации.Ошибка,,, 
            "Ошибка проведения документа по файлу: " + ТекФайл.ПолноеИмя + ". " + ОписаниеОшибки());
    КонецПопытки;
    
КонецЦикла;

Преимущества метода ТабличныйДокумент.Прочитать():

  1. Полная независимость от разрядности сервера 1С (32/64 bit) и наличия установленного пакета Microsoft Office.
  2. Корректная работа на серверах под управлением ОС Linux.
  3. Отсутствие риска зависания фоновых процессов в памяти сервера.
  4. Существенный прирост скорости чтения объемных таблиц по сравнению с COM-автоматизацией.
← На главную