Работа с файлами Microsoft Excel в клиент-серверном варианте работы 1С часто преподносит сюрпризы. Программисты сталкиваются с тем, что код, идеально работающий на локальном компьютере («на файле»), внезапно выдает ошибки при переносе в серверный контекст. Ошибка «Sorry, we couldn't find...» или «Не удалось создать COM-объект» становится настоящей головной болью.
В этой статье мы подробно разберем, почему Excel капризничает на сервере, проанализируем различные способы чтения данных и выясним, как правильно организовать взаимодействие между клиентом и сервером без потери производительности.
Разберем ситуацию, когда при вызове метода Open возникает исключительная ситуация. Основная причина кроется в том, что серверный процесс 1С (обычно запущенный под пользователем USR1CV8) работает в изолированном сеансе. Для него пути типа D:\Выгрузка\doc.xls могут быть либо недоступны из-за прав доступа, либо этот путь ведет на локальный диск сервера, в то время как файл находится на компьютере пользователя.
Кроме того, Microsoft Excel — это интерактивное приложение. Оно не предназначено для работы в фоновом режиме на сервере. Рассмотрим основные пути решения этой задачи.
Это самый современный и эффективный метод. Начиная с версии 8.3, 1С умеет читать файлы .xlsx и .xls напрямую, не используя установленный Microsoft Office. Это избавляет от необходимости настраивать DCOM и покупать лицензии на Office для сервера.
Проанализируем пример кода, который позволяет прочитать файл на сервере:
&НаСервере
Процедура ПрочитатьЭксельНаСервере(ПутьКФайлу)
// Создаем объект табличного документа
ТД = Новый ТабличныйДокумент;
Попытка
// Читаем файл. Второй параметр указывает на способ чтения значений
ТД.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
Сообщить("Ошибка при чтении файла: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Далее работаем с ТД как с обычной таблицей
Для НомерСтроки = 1 По ТД.ВысотаТаблицы Цикл
Значение = ТД.Область(НомерСтроки, 1).Текст;
// Обработка данных...
КонецЦикла;
КонецПроцедуры
Преимущества этого метода:
excel.exe в памяти сервера.Если вы все же решили использовать серверные процедуры для обработки файла, помните: сервер не имеет прямого доступа к файлам на вашем локальном диске C:. Файл необходимо сначала «передать» на сервер.
Рассмотрим по шагам, как это реализовать:
&НаКлиенте
Процедура ВыгрузитьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Если Диалог.Выбрать() Тогда
ДанныеФайла = Новый ДвоичныеДанные(Диалог.ПолноеИмяФайла);
АдресХранилища = ПоместитьВоВременноеХранилище(ДанныеФайла, УникальныйИдентификатор);
ОбработатьФайлНаСервере(АдресХранилища);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресХранилища)
Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xls");
Данные.Записать(ИмяВременногоФайла);
// Теперь файл физически есть на диске сервера, и его можно открыть
// Например, через ТабличныйДокумент или COM (если он настроен)
ТД = Новый ТабличныйДокумент;
ТД.Прочитать(ИмяВременногоФайла);
УдалитьФайлы(ИмяВременногоФайла); // Не забываем чистить за собой
КонецПроцедуры
Бывают случаи, когда нужно использовать специфические функции Excel (макросы, сложное форматирование), которые не поддерживает ТабличныйДокумент. В этом случае придется настраивать сервер.
Выясним причину, по которой Excel часто выдает ошибку на сервере даже при наличии прав. Для корректной инициализации COM-объекта Excel под системной учетной записью необходимо наличие папок Desktop. Выясним, где их нужно создать:
C:\Windows\SysWOW64\config\systemprofile\DesktopC:\Windows\System32\config\systemprofile\DesktopВажно: У пользователя, под которым запускается сервер 1С, должны быть полные права на эти папки. Без этого Excel.Application может просто не стартовать в фоновом сеансе.
Часто возникает вопрос: как обработать данные на сервере (так как там запросы и база), а потом передать их в Excel на клиенте (так как Excel установлен только у пользователя)? Напрямую объект ТаблицаЗначений передать на клиент нельзя.
Рассмотрим наиболее надежный метод — преобразование ТЗ в массив структур:
&НаСервере
Функция ПолучитьДанныеДляExcel()
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование ИЗ Справочник.Номенклатура ГДЕ ... ");
ТЗ = Запрос.Выполнить().Выгрузить();
МассивРезультат = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из ТЗ.Колонки Цикл
СтруктураСтроки.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
КонецЦикла;
МассивРезультат.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
&НаКлиенте
Процедура СформироватьExcel(Команда)
Данные = ПолучитьДанныеДляExcel();
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Add();
Лист = Книга.Sheets(1);
НомерСтроки = 1;
Для Каждого СтрокаДанных Из Данные Цикл
Лист.Cells(НомерСтроки, 1).Value = СтрокаДанных.Наименование;
Лист.Cells(НомерСтроки, 2).Value = СтрокаДанных.Код;
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
Excel.Visible = Истина;
КонецПроцедуры
Использование массива структур позволяет легко обходить ограничения контекстов 1С. Также можно использовать Временное хранилище для передачи больших таблиц значений, как было предложено в обсуждении темы. В этом случае на сервере мы помещаем ТЗ в хранилище, а на клиенте в цикле запрашиваем данные порциями (строками), чтобы не перегружать память.
Проанализируем еще несколько важных моментов при работе с Excel на сервере:
Excel.Quit() и обнуляйте переменную (Excel = 0;). Иначе процессы будут висеть в памяти до перезагрузки сервера.ActiveX Data Objects (ADO). Это работает быстрее, так как обращается к файлу как к базе данных.ПолучитьИмяВременногоФайла().Подведем итог: если ваша задача — просто прочитать данные из файла, используйте ТабличныйДокумент. Это избавит вас от 90% технических проблем с правами доступа и настройкой DCOM. Если же требуется сложная генерация отчета по шаблону на стороне клиента, используйте передачу данных через Массив структур или Временное хранилище.