Как исправить ошибку "Microsoft Excel не удается получить доступ к файлу" при открытии Excel на сервере 1С?

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

При разработке на платформе 1С часто возникает задача обработки файлов Excel на стороне сервера. Нередко программисты начинают с того, что ищут готовый шаблон простой обработки загрузки из excel, чтобы быстро реализовать импорт. Однако стандартный подход с использованием COM-объекта может преподнести неприятный сюрприз: код, который отлично работает на клиенте, на сервере завершается с ошибкой "Приложению Microsoft Excel не удается получить доступ к файлу...". Давайте разберем, почему это происходит и как это исправить, рассмотрев несколько подходов — от исправления окружения до полного отказа от COM.

Причина №1: Неправильная архитектура. Доступ к файлу с сервера

Первое, что нужно понять — это различие между клиентским и серверным контекстом. Когда пользователь запускает обработку на своем компьютере (клиенте), его локальные диски, такие как D:, доступны. Однако сервер 1С — это отдельный процесс, часто на другой машине, который ничего не знает о локальных дисках пользователя.

Код, подобный этому, обречен на провал в серверном вызове:


&НаСервере
Процедура ОткрытьНаСервере()
    // Этот путь существует только на клиентской машине, но не на сервере!
    ИмяФайла = "D:\1C\Обработки\f1.xlsx"; 
    Попытка
        Excel = Новый COMОбъект("Excel.Application");
        WB = Excel.Workbooks.Open(ИмяФайла); // Ошибка!
      // ...
    Исключение
        Сообщить(ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

Правильный подход — передать сам файл с клиента на сервер. Для этого в платформе 1С существует специальный механизм временного хранилища. Разберем по шагам, как это реализовать.

  1. На клиенте: Пользователь выбирает файл в стандартном диалоге. Платформа помещает содержимое этого файла во временное хранилище и возвращает нам адрес этого хранилища.
  2. Передача на сервер: Адрес из временного хранилища (это обычная строка) передается в качестве параметра в серверную процедуру.
  3. На сервере: С помощью полученного адреса мы извлекаем двоичные данные файла. Затем эти данные записываются во временный файл уже на диске сервера. Путь к этому временному файлу и используется для дальнейшей работы.

Посмотрим на пример кода, который реализует этот алгоритм:


// Шаг 1: Инициируем выбор файла на клиенте
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
    
    // Создаем описание оповещения, которое выполнится после выбора файла
    ОписаниеОповещения = Новый Описание ОписаниеОповещения("ОбработатьВыбранныеФайлы", ЭтаФорма);
    
    ДиалогОткрытияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    ДиалогОткрытияФайла.МножественныйВыбор = Ложь;
    ДиалогОткрытияФайла.Заголовок = "Выберите файл Excel";
    ДиалогОткрытияФайла.Фильтр = "Файлы Excel (*.xls;*.xlsx)|*.xls;*.xlsx";
    
    // Асинхронная функция, которая откроет диалог и поместит файл в хранилище
    НачатьПомещениеФайлов(ОписаниеОповещения, , ДиалогОткрытияФайла, Истина, УникальныйИдентификатор);
    
КонецПроцедуры

// Шаг 2: Обрабатываем результат выбора файла на клиенте и передаем на сервер
&НаКлиенте
Процедура ОбработатьВыбранныеФайлы(ПомещенныеФайлы, ДополнительныеПараметры) Экспорт
    
    Если ПомещенныеФайлы = Неопределено Тогда
        Возврат; // Пользователь отменил выбор
    КонецЕсли;
    
    // ПомещенныеФайлы - это массив, даже если файл один
    ПереданныйФайл = ПомещенныеФайлы[0];
    
    // Вызываем серверную процедуру, передавая адрес хранения и имя файла
    ПрочитатьФайлНаСервере(ПереданныйФайл.Хранение, ПереданныйФайл.Имя);
    
КонецПроцедуры

// Шаг 3: Получаем данные на сервере и работаем с ними
&НаСервере
Процедура ПрочитатьФайлНаСервере(АдресВХранилище, ИмяФайла) 
    
    // Получаем двоичные данные из временного хранилища по адресу
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
        
    // Создаем временный файл на диске сервера
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    // Теперь у нас есть путь к файлу, с которым может работать серверный код
    Попытка
        Excel = Новый COMОбъект("Excel.Application");
        WB = Excel.Workbooks.Open(ИмяВременногоФайла); // Работаем с временным файлом
        ЛистЭксель = WB.Worksheets(1);
        
        // ... здесь ваш код обработки файла ...
        // Для более сложных задач может быть полезна загрузка файлов Excel и CSV, которая умеет читать все листы
        
        Сообщить("Файл успешно открыт на сервере!");

        WB.Close(Ложь); // Закрываем книгу без сохранения
        Excel.Quit();   // Закрываем приложение Excel
    Исключение
        Сообщить(ОписаниеОшибки());
    КонецПопытки;

    // Не забываем удалить временный файл после использования
    УдалитьФайлы(ИмяВременногоФайла);
    
КонецПроцедуры

Причина №2: Проблема окружения. Отсутствие папки "Desktop"

Предположим, вы все сделали правильно: передали файл на сервер, получили временный путь и пытаетесь его открыть. Но ошибка "Приложению Microsoft Excel не удается получить доступ к файлу..." все равно появляется! В чем же дело на этот раз?

Проблема кроется в особенностях работы COM-автоматизации приложений Microsoft Office. Сервер 1С (процесс rphost.exe) обычно работает от имени специальной системной учетной записи, например, USR1CV8. У этой учетной записи нет полноценного пользовательского профиля, к которому привыкли интерактивные пользователи. При запуске в неинтерактивном режиме (как это происходит на сервере) приложение Excel пытается получить доступ к некоторым папкам из профиля пользователя, в частности, к папке рабочего стола (Desktop). Если этой папки нет, приложение аварийно завершается, генерируя ошибку доступа к файлу, хотя реальная причина — в окружении.

Решение — создать эту папку вручную на сервере 1С.

Вам необходимо зайти на сервер, где установлен сервер 1С:Предприятия, и создать папку Desktop в системном профиле. Путь зависит от разрядности вашей операционной системы Windows:

После создания этой папки (права на нее должны быть у системных служб по умолчанию) перезапускать сервер 1С или службы не требуется. Попробуйте выполнить вашу обработку снова — с большой вероятностью, ошибка исчезнет.

Решение №3: Альтернативный и рекомендуемый способ. Отказ от COM

Использование COM-автоматизации на сервере — не самая лучшая практика. У этого подхода есть существенные недостатки, поэтому часто лучше использовать инструменты для универсальной загрузки из Excel, где наличие самого Excel на компьютере не требуется. Основные проблемы COM:

К счастью, платформа 1С предоставляет встроенные средства для чтения файлов Excel без привлечения сторонних приложений. Основной инструмент для этого — объект ТабличныйДокумент, который позволяет реализовать полноценный импорт данных из форматов xls, xlsx и даже ods.

Давайте перепишем наш серверный код с использованием этого объекта. Он становится значительно проще и надежнее.


&НаСервере
Процедура ПрочитатьФайлНаСервере(АдресВХранилище, ИмяФайла)
    
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    // Используем встроенный объект 1С
    ТабДок = Новый ТабличныйДокумент;
    
    Попытка
        // Метод Прочитать() сам распознает формат xls или xlsx
        ТабДок.Прочитать(ИмяВременногоФайла);
        
        // Теперь все данные из файла находятся в объекте ТабличныйДокумент.
        // Можно обходить ячейки, получать значения и т.д.
        // Например, получим значение из ячейки R1C1 (A1)
        Область = ТабДок.Область("R1C1");
        ЗначениеЯчейки = Область.Текст;
        
        Сообщить("Файл успешно прочитан с помощью ТабличногоДокумента! Значение A1: " + ЗначениеЯчейки);
        
    Исключение
        Сообщить("Ошибка при чтении файла: " + ОписаниеОшибки());
    КонецПопытки;
    
    УдалитьФайлы(ИмяВременногоФайла);
    
КонецПроцедуры

Работа с многолистовыми файлами:

Объект ТабличныйДокумент также поддерживает работу с файлами, содержащими несколько листов. Каждый лист Excel загружается как именованная область. Имена областей совпадают с именами листов (с заменой пробелов на символы подчеркивания).


// ... код чтения файла в ТабДок ...

// Перебираем все листы (области) в документе
Для каждого ОбластьЛиста Из ТабДок.Области Цикл
    
    ИмяЛиста = ОбластьЛиста.Имя;
    Сообщить("Обрабатываем лист: " + ИмяЛиста);
    
    // Можно получить конкретную ячейку на этом листе
    // Например, ячейку B2 (вторая строка, второй столбец)
    Значение = ТабДок.Область(2, 2, 2, 2).Текст; // Для первого листа
    
    // Чтобы получить ячейку с конкретного листа, нужно использовать его как область
    ЯчейкаНаЛисте = ОбластьЛиста.Область(2, 2);
    ЗначениеСЛиста = ЯчейкаНаЛисте.Текст;

КонецЦикла;

Таким образом, использование объекта ТабличныйДокумент не только решает проблему с доступом к файлу на сервере, но и делает ваш код более надежным. Этот способ является предпочтительным.

← На главную