При работе с платформой 1С:Предприятие 8.3, особенно в клиент-серверном варианте с управляемыми формами, задача загрузки и обработки файлов Excel на сервере может стать непростой. Часто разработчики сталкиваются с проблемами при попытке использовать COM-обобъекты Microsoft Excel напрямую на сервере 1С. В этом подробном руководстве мы рассмотрим основные причины возникновения таких проблем и предложим несколько надежных решений, основанных на опыте и рекомендациях сообщества 1С. Для решения этой задачи есть универсальный импорт любых данных из Excel.
Прежде чем перейти к решениям, давайте разберем, почему попытки создания и использования COM-объекта Excel.Application на сервере 1С в клиент-серверном режиме часто приводят к ошибкам, таким как "Excel Application не создан!!" или "Файл перемещен или удален!". Автор темы изначально столкнулся именно с этой ситуацией, когда его код отлично работал в файловой базе, но отказывался функционировать на клиент-серверной архитектуре.
Основная проблема кроется в нескольких факторах:
USR1CV8 на Windows или специальный пользователь на Linux), должна иметь соответствующие права на запуск и доступ к DCOM-серверу Microsoft Excel Application. Эти права настраиваются через оснастку "Службы компонентов" (Component Services) в Windows. Помимо этого, у той же учетной записи должны быть достаточные права NTFS на чтение и запись в каталог, где временно хранится файл Excel. Отсутствие хотя бы одного из этих прав приведет к ошибкам.Автор представил свой исходный код, который демонстрирует попытку использования COM-объекта Excel на сервере:
&НаКлиенте
Процедура Загрузить(Команда)
// Объект.ИмяФайла - это, предположительно, путь к файлу на клиентской машине
Файл = Новый ДвоичныеДанные(ОБъект.ИмяФайла);
// Помещаем двоичные данные файла во временное хранилище 1С
Адрес = ПоместитьВоВременноеХранилище(Файл, ЭтаФорма.УникальныйИдентификатор);
// Вызываем серверную процедуру для обработки файла
ЗагрузитьНаСервере(Адрес);
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьНаСервере (ФайлВХранилище)
// Попытка создания COM-объекта Excel
Попытка
ex = ПолучитьCOMобъект("","Excel.Application");
Исключение
Сообщить("Excel Application не создан!!");
Возврат;
КонецПопытки;
// Получаем двоичные данные из временного хранилища
ДвоичныеДанные = ПолучитьИзВременногоХранилища(ФайлВХранилище);
// Определяем временный путь на сервере для записи файла
тПуть = КаталогВременныхФайлов + "123.xlsx"; // для примера, лучше использовать уникальное имя
// Записываем файл на диск сервера
ДвоичныеДанные.Записать(тПуть);
// Попытка открыть файл Excel через COM-объект
Попытка
ex.workbooks.open.Open(тПуть,,Истина,,,,,,,,Ложь);
Исключение
Сообщить("Файл перемещен или удален!"); // Это сообщение часто указывает на проблемы с COM-объектом
Возврат;
КонецПопытки;
// Дальнейшая обработка файла...
КонецПроцедуры
Как мы видим из обсуждения, даже когда файл физически существует на сервере (проверено методом Файл.Существует(), как показано в сообщении 41), попытка его открытия через COM-объект может завершиться неудачей с ошибкой "Файл перемещен или удален!". Это является сильным индикатором проблем с правами COM-объекта Excel или его способностью функционировать в серверной среде 1С.
Наиболее надежный, простой и рекомендованный способ загрузки данных из файлов Excel (.xlsx, .xls) на сервере 1С, особенно в управляемых формах, — это использование метода Прочитать() объекта ТабличныйДокумент. Этот подход избавляет нас от всех проблем, связанных с COM-объектами Excel, их лицензированием и правами доступа на сервере. Для этой задачи есть кроссплатформенный импорт Excel без Microsoft Office.
ТабличныйДокумент.Прочитать():
Если ваш файл Excel содержит несколько листов, ТабличныйДокумент.Прочитать() загружает их все. После загрузки данные каждой страницы становятся доступными как "именованные области" (Named Areas) внутри объекта ТабличныйДокумент. Вы можете легко обращаться к конкретным листам по их названиям или индексам. Затем вы можете выделить нужную область листа и обрабатывать ее содержимое.
Давайте рассмотрим подробный пример кода, который демонстрирует загрузку файла Excel, переданного с клиента, в ТабличныйДокумент на сервере и последующую обработку его данных:
&НаКлиенте
Процедура Загрузить(Команда)
// Предполагаем, что ОБъект.ИмяФайла содержит путь к выбранному на клиенте файлу.
// Если файла нет, можно использовать НачатьПомещениеФайла для выбора файла
// и получения его двоичных данных или адреса во временном хранилище.
// Проверяем существование файла на клиенте
ФайлКлиента = Новый Файл(ОБъект.ИмяФайла);
Если Не ФайлКлиента.Существует() Тогда
Сообщить("Файл '" + ОБъект.ИмяФайла + "' не найден на клиенте. Выберите файл.");
Возврат;
КонецЕсли;
// Получаем двоичные данные файла
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ОБъект.ИмяФайла);
// Помещаем двоичные данные файла во временное хранилище 1С
// Это позволяет безопасно передать файл с клиента на сервер.
АдресФайлаВХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, ЭтаФорма.УникальныйИдентификатор);
// Вызываем серверную процедуру для обработки файла
ЗагрузитьExcelНаСервере(АдресФайлаВХранилище);
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьExcelНаСервере (АдресФайлаВХранилище)
// 1. Получаем двоичные данные файла из временного хранилища
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайлаВХранилище);
// 2. Определяем временный путь для записи файла на сервере
// Используем уникальный идентификатор для имени файла, чтобы избежать конфликтов
ПеременныйПутьНаСервере = КаталогВременныхФайлов + УникальныйИдентификатор.НовыйУникальныйИдентификатор().Строка() + ".xlsx";
// 3. Записываем двоичные данные во временный файл на сервере
Попытка
ДвоичныеДанныеФайла.Записать(ПеременныйПутьНаСервере);
Исключение
Сообщить("Ошибка при записи файла на сервер: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// 4. Проверяем существование файла на диске сервера (диагностический шаг)
// Это важно для подтверждения, что файл успешно записан и доступен для чтения.
ФайлНаДиске = Новый Файл(ПеременныйПутьНаСервере);
Если НЕ ФайлНаДиске.Существует() Тогда
Сообщить("Файл не существует по пути: " + ПеременныйПутьНаСервере + ". Проверьте права доступа.");
Возврат;
КонецЕсли;
Сообщить("Файл успешно записан на сервере по пути: " + ПеременныйПутьНаСервере);
// 5. Загружаем данные Excel в ТабличныйДокумент
ТабДок = Новый ТабличныйДокумент;
Попытка
// Метод Прочитать() автоматически определяет тип файла (xls, xlsx)
ТабДок.Прочитать(ПеременныйПутьНаСервере, СпособЧтенияЗначенийТабличногоДокумента.Авто);
Исключение
Сообщить("Ошибка при чтении файла Excel методом ТабличныйДокумент.Прочитать(): " + ОписаниеОшибки());
// Попытаемся удалить файл даже при ошибке чтения
Попытка ФайлНаДиске.Удалить(); Исключение КонецПопытки;
Возврат;
КонецПопытки;
Сообщить("Файл успешно прочитан в ТабличныйДокумент.");
// 6. Пример обработки данных (извлечение данных с листов)
// После загрузки, все листы Excel доступны как именованные области ТабличногоДокумента.
// Мы можем перебрать эти области или обратиться к ним по имени.
ТаблицаДанныхДляОбработки = Новый ТаблицаЗначений;
ТаблицаДанныхДляОбработки.Колонки.Добавить("Наименование"); // Пример: добавляем колонки для будущих данных
ТаблицаДанныхДляОбработки.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаДанныхДляОбработки.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
// Перебираем именованные области (листы) в ТабличномДокументе
Для Каждого Область ИЗ ТабДок.ИменованныеОбласти Цикл
Сообщить("Обнаружен лист Excel с именем: " + Область.Имя + ". Начинаем обработку.");
// Получаем область текущего листа (можно уточнить диапазон, например "Лист1!A1:Z100")
// Если нужна вся область листа, достаточно использовать Область.Имя
ОбластьЛиста = ТабДок.ПолучитьОбласть(Область.Имя);
// Пример: Обход строк и колонок в текущем листе
// Предположим, что данные начинаются со 2-й строки (после заголовков)
Для НомерСтроки = ОбластьЛиста.ВерхняяСтрока + 1 По ОбластьЛиста.НижняяСтрока Цикл
НоваяСтрокаТЗ = ТаблицаДанныхДляОбработки.Добавить();
// Читаем данные из ячеек текущей строки.
// Обращаемся к ячейкам относительно общей таблицы, а не относительно области.
// Предположим, что колонка 1 содержит Наименование, 2 - Количество, 3 - Цена.
ЯчейкаНаименование = ТабДок.Область(НомерСтроки, ОбластьЛиста.ЛеваяКолонка + 0); // +0 для первой колонки листа
ЯчейкаКоличество = ТабДок.Область(НомерСтроки, ОбластьЛиста.ЛеваяКолонка + 1); // +1 для второй колонки листа
ЯчейкаЦена = ТабДок.Область(НомерСтроки, ОбластьЛиста.ЛеваяКолонка + 2); // +2 для третьей колонки листа
Если ЯчейкаНаименование.Значение <> Неопределено Тогда
НоваяСтрокаТЗ.Наименование = Строка(ЯчейкаНаименование.Значение);
КонецЕсли;
Если ЯчейкаКоличество.Значение <> Неопределено Тогда
НоваяСтрокаТЗ.Количество = Число(ЯчейкаКоличество.Значение);
КонецЕсли;
Если ЯчейкаЦена.Значение <> Неопределено Тогда
НоваяСтрокаТЗ.Цена = Число(ЯчейкаЦена.Значение);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Сообщить("Данные из ТабличногоДокумента перенесены в ТаблицуЗначений. Всего строк: " + ТаблицаДанныхДляОбработки.Количество());
// После обработки, временный файл на сервере лучше удалить
Попытка
ФайлНаДиске.Удалить();
Сообщить("Временный файл удален: " + ПеременныйПутьНаСервере);
Исключение
Сообщить("Не удалось удалить временный файл: " + ОписаниеОшибки());
КонецПопытки;
// Теперь ТаблицаДанныхДляОбработки содержит все необходимые данные
// и может быть использована для создания элементов справочников, документов и т.п.
// Например, переберем строки и создадим элементы справочника:
Для Каждого СтрокаТЗ Из ТаблицаДанныхДляОбработки Цикл
// Создаем новый элемент справочника "Номенклатура" (пример)
// НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
// НовыйЭлемент.Наименование = СтрокаТЗ.Наименование;
// НовыйЭлемент.Количество = СтрокаТЗ.Количество;
// НовыйЭлемент.Цена = СтрокаТЗ.Цена;
// Попытка
// НовыйЭлемент.Записать();
// Сообщить("Создан элемент номенклатуры: " + НовыйЭлемент.Наименование);
// Исключение
// Сообщить("Ошибка при создании элемента: " + ОписаниеОшибки());
// КонецПопытки;
КонецЦикла;
Сообщить("Загрузка данных завершена.");
КонецПроцедуры
Если возможности метода ТабличныйДокумент.Прочитать() (например, отсутствие поддержки сложных макросов, специфического форматирования, специфических функций Excel) оказываются недостаточными для вашей задачи, или если вам нужно использовать именно функционал COM-объектов Excel, то наиболее безопасным и работоспособным вариантом является обработка файла на стороне клиента. В этом случае все взаимодействие с Microsoft Excel происходит на машине пользователя, где установлен Office, а на сервер передаются уже обработанные и структурированные данные.
Excel.Application на клиенте.ТаблицаЗначений, Массив, Структура).Рассмотрим пример кода, демонстрирующего этот подход:
&НаКлиенте
Процедура Загрузить(Команда)
// 1. Получаем путь к файлу на клиенте.
// ОБъект.ИмяФайла - это, предположительно, путь к файлу, выбранному пользователем
Перем ПутьКФайлуНаКлиенте;
Если ЗначениеЗаполнено(ОБъект.ИмяФайла) Тогда
ПутьКФайлуНаКлиенте = ОБъект.ИмяФайла;
Иначе
Сообщить("Путь к файлу не указан.");
Возврат;
КонецЕсли;
// Проверяем существование файла на клиенте
ФайлКлиента = Новый Файл(ПутьКФайлуНаКлиенте);
Если Не ФайлКлиента.Существует() Тогда
Сообщить("Файл по пути '" + ПутьКФайлуНаКлиенте + "' не существует. Проверьте путь.");
Возврат;
КонецЕсли;
// 2. Создаем COM-объект Excel на клиенте.
// Вариант 1: Через ПолучитьCOMобъект
Попытка
Excel = ПолучитьCOMобъект("","Excel.Application");
Исключение
Сообщить("Не удалось создать COM-обобъект Excel. Убедитесь, что Microsoft Excel установлен на этом компьютере: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Вариант 2 (также рабочий): Через Новый COMОбъект
// Попытка
// Excel = Новый COMОбъект("Excel.Application");
// Исключение
// Сообщить("Не удалось создать COM-ообъект Excel. Убедитесь, что Microsoft Excel установлен на этом компьютере: " + ОписаниеОшибки());
// Возврат;
// КонецПопытки;
Excel.Visible = Ложь; // Скрываем Excel, чтобы он не открывался на экране пользователя
// 3. Открываем и читаем файл Excel.
Попытка
// Открытие книги
Книга = Excel.WorkBooks.Open(ПутьКФайлуНаКлиенте,,Истина,,,,,,,,Ложь);
// Или более простой вариант, как в сообщении 46: Книга = Excel.WorkBooks.Open(ПутьКФайлуНаКлиенте);
// Выбираем первый лист (или любой другой по индексу/имени)
Лист = Книга.WorkSheets(1); // Первый лист
// Или по имени: Лист = Книга.WorkSheets("МойЛист");
Лист.Select();
Исключение
Сообщить("Ошибка при открытии файла Excel: " + ОписаниеОшибки());
Если Книга <> Неопределено Тогда
Книга.Close();
КонецЕсли;
Excel.Quit();
Возврат;
КонецПопытки;
// 4. Преобразуем данные в коллекцию 1С (ТаблицаЗначений).
ТаблицаДанныхДляПередачи = Новый ТаблицаЗначений;
ТаблицаДанныхДляПередачи.Колонки.Добавить("Код");
ТаблицаДанныхДляПередачи.Колонки.Добавить("Наименование");
ТаблицаДанныхДляПередачи.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаДанныхДляПередачи.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
// Пример: Чтение данных из первых 3 колонок, начиная со 2-й строки
// Определим используемый диапазон. Можно получить последнюю используемую строку.
ПоследняяСтрока = Лист.UsedRange.Rows.Count; // Или Лист.Cells.SpecialCells(11).Row
Если ПоследняяСтрока = 0 Тогда // Если лист пуст
Сообщить("Файл Excel пуст или не содержит данных.");
Книга.Close(Ложь); // Закрываем без сохранения
Excel.Quit();
Возврат;
КонецЕсли;
Для НомерСтроки = 2 По ПоследняяСтрока Цикл // Начинаем со 2-й строки, если первая строка - заголовки
НоваяСтрока = ТаблицаДанныхДляПередачи.Добавить();
НоваяСтрока.Код = Лист.Cells(НомерСтроки, 1).Value;
НоваяСтрока.Наименование = Лист.Cells(НомерСтроки, 2).Value;
НоваяСтрока.Количество = Лист.Cells(НомерСтроки, 3).Value;
НоваяСтрока.Цена = Лист.Cells(НомерСтроки, 4).Value; // Пример: 4-я колонка
КонецЦикла;
Книга.Close(Ложь); // Закрываем книгу Excel без сохранения изменений
Excel.Quit(); // Завершаем работу приложения Excel
Сообщить("Данные успешно прочитаны на клиенте. Количество строк: " + ТаблицаДанныхДляПередачи.Количество());
// 5. Передаем ТаблицуЗначений на сервер для дальнейшей обработки.
// Параметры можно передавать напрямую или, для больших объемов, через временное хранилище.
ОбработатьДанныеНаСервере(ТаблицаДанныхДляПередачи);
КонецПроцедуры
// Вспомогательная процедура для выбора файла на клиенте
&НаКлиенте
Процедура ВыбратьФайлНаКлиенте(Команда)
ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогВыбораФайла.Заголовок = "Выберите файл Excel";
ДиалогВыбораФайла.Фильтр = "Файлы Excel (*.xls;*.xlsx)|*.xls;*.xlsx";
ДиалогВыбораФайла.МножественныйВыбор = Ложь;
Если ДиалогВыбораФайла.Выбрать() Тогда
ОБъект.ИмяФайла = ДиалогВыбораФайла.ПолноеИмяФайла;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ОбработатьДанныеНаСервере (ТаблицаДанныхИзExcel)
// 6. На сервере используем полученные данные для создания элементов справочников, документов и т.п.
Сообщить("На сервере получено " + ТаблицаДанныхИзExcel.Количество() + " строк данных.");
Для Каждого СтрокаДанных Из ТаблицаДанныхИзExcel Цикл
Попытка
// Пример: Поиск или создание элемента справочника "Номенклатура"
// СправочникНоменклатура = Справочники.Номенклатура.НайтиПоНаименованию(СтрокаДанных.Наименование, Истина);
// Если СправочникНоменклатура = Неопределено Тогда
// НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
// НовыйЭлемент.Наименование = СтрокаДанных.Наименование;
// // Дополнительные свойства
// // НовыйЭлемент.Код = СтрокаДанных.Код;
// НовыйЭлемент.Записать();
// Сообщить("Создан новый элемент номенклатуры: " + НовыйЭлемент.Наименование);
// Иначе
// Сообщить("Элемент номенклатуры уже существует: " + СправочникНоменклатура.Наименование);
// КонецЕсли;
// Пример: Создание документа "Поступление товаров и услуг"
// НовыйДокумент = Документы.ПоступлениеТоваровУслуг.СоздатьДокумент();
// НовыйДокумент.Дата = ТекущаяДата();
// НовыйДокумент.Организация = ПолучитьЗначениеПараметра("ОсновнаяОрганизация"); // Пример: из настроек
// НоваяСтрокаТЧ = НовыйДокумент.Товары.Добавить();
// НоваяСтрокаТЧ.Номенклатура = СправочникНоменклатура; // Или ищем по коду/наименованию
// НоваяСтрокаТЧ.Количество = СтрокаДанных.Количество;
// НоваяСтрокаТЧ.Цена = СтрокаДанных.Цена;
// НоваяСтрокаТЧ.Сумма = СтрокаДанных.Количество * СтрокаДанных.Цена;
// НовыйДокумент.Записать();
// Сообщить("Создан документ поступления для: " + СтрокаДанных.Наименование);
Исключение
Сообщить("Ошибка при обработке строки данных '" + СтрокаДанных.Наименование + "': " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Сообщить("Серверная обработка данных Excel завершена.");
КонецПроцедуры
В процессе работы с файлами на сервере, особенно при отладке, критически важно уметь диагностировать проблемы. Автор темы столкнулся с тем, что точка останова на сервере не срабатывала (сообщение 18), что затрудняло анализ.
Используйте метод Существует() объекта Файл, чтобы убедиться, что файл физически присутствует на диске сервера по указанному пути и доступен для чтения пользователем 1С:Предприятия (USR1CV8).
Например:
&НаСервере
Процедура ПроверитьФайл(тПуть)
ФайлНаДиске = Новый Файл(тПуть);
Если ФайлНаДиске.Существует() Тогда
Сообщить("Файл существует по пути: " + тПуть);
Иначе
Сообщить("Файл НЕ существует по пути: " + тПуть);
КонецЕсли;
КонецПроцедуры
В ходе обсуждения автор несколько раз ошибочно пытался применить метод Существует() к объектам, которые не являются объектами Файл (сообщения 20, 22). Запомните, что метод Существует() применим только к объекту Файл.
Некорректное использование (как в теме):
Сообщить(Файл.Существует); // Файл здесь - ДвоичныеДанные, а не объект Файл
Сообщить(тПуть.Существует); // тПуть здесь - Строка, а не объект Файл
Правильное использование:
// Перед использованием:
// ДвоичныеДанные = ПолучитьИзВременногоХранилища(ФайлВХранилище);
// тПуть = КаталогВременныхФайлов + "уникальное_имя.xlsx";
// ДвоичныеДанные.Записать(тПуть);
ФайлДляПроверки = Новый Файл(тПуть);
Сообщить(ФайлДляПроверки.Существует()); // Вот так правильно
Убедитесь, что учетная запись, под которой работает агент сервера 1С:Предприятия (обычно это USR1CV8 на Windows), имеет полные права доступа (чтение, запись, изменение) к каталогам, где временно сохраняется файл Excel (например, КаталогВременныхФайлов или любой другой каталог, куда вы сохраняете файл).
Если сервер 1С запущен на другом физическом сервере (как предполагал автор в сообщении 16), это может усложнить настройку прав и отладку, так как требуется доступ к настройкам удаленного сервера.
Если вы все же настаиваете на использовании COM-объектов Excel на сервере, вам придется настроить права DCOM. Это делается через оснастку "Службы компонентов" (Component Services) в Windows:
USR1CV8. На вкладке "Удостоверение" попробуйте выбрать "Указанный пользователь" и указать пользователя с административными правами или пользователя службы 1С.Мы рассмотрели различные подходы к загрузке и обработке файлов Excel на сервере 1С в клиент-серверном режиме. На основе анализа проблемы и предложенных решений, мы можем выделить следующие рекомендации:
ТабличныйДокумент.Прочитать(): Если ваша задача сводится к извлечению структурированных данных из Excel-файла для последующей записи в базу 1С, то метод ТабличныйДокумент.Прочитать() является наиболее предпочтительным. Он универсален, не требует установки Office на сервере и позволяет избежать большинства проблем с COM-объектами.ТабличныйДокумент), то обрабатывайте файл на стороне клиента, преобразуйте данные в ТаблицуЗначений или аналогичную структуру и передавайте уже подготовленные данные на сервер. Этот подход является безопасным и обходит все сложности серверной автоматизации Office.Новый Файл(Путь).Существует() и сообщений для отладки, а также плотное взаимодействие с системным администратором.Помните, что платформа 1С предоставляет мощные встроенные инструменты для работы с данными, и часто можно найти решение, не прибегая к сложным и потенциально проблемным внешним интеграциям на сервере. При необходимости можно воспользоваться готовой обработкой для импорта данных из Excel без установки Microsoft Office.