При разработке собственных механизмов выгрузки данных в формат Excel программисты часто сталкиваются с ситуацией, когда код, успешно работающий в файловой базе или на стороне клиента, выдает ошибку «Ошибка при выполнении файловой операции... Путь %1 не найден» при работе в клиент-серверном режиме. В этой статье мы подробно разберем причины возникновения этой проблемы, проанализируем особенности контекста выполнения кода и рассмотрим наиболее эффективные способы передачи сформированных файлов пользователю.
Первым делом проанализируем ситуацию, описанную в исходном сообщении. Проблема заключается в фундаментальном различии между клиентским и серверным контекстом выполнения кода в 1С:Предприятии 8.3. Рассмотрим подробнее основные причины:
C:\Обмен\Организации.xls в коде, который выполняется на сервере (в процедуре с директивой &НаСервере), система ищет этот путь на физическом сервере, где установлена служба «Агент сервера 1С:Предприятия». Если на сервере нет диска C или отсутствует папка «Обмен», запись файла станет невозможной.USR1CV8). У этого пользователя может не быть прав на запись в корень диска C или в произвольные папки.Записать() объекта ТабличныйДокумент не создает промежуточные папки автоматически. Если родительского каталога не существует, возникнет ошибка файловой операции.В современных конфигурациях на управляемых формах правильным подходом считается формирование файла на сервере с последующей передачей его на клиент через временное хранилище. Рассмотрим этот алгоритм по шагам.
Сначала на сервере мы формируем табличный документ, сохраняем его во временный файл и помещаем в хранилище:
&НаСервере
Функция СформироватьФайлНаСервере()
ТабДок = Новый ТабличныйДокумент;
// ... здесь код заполнения табличного документа (вывод макета, областей) ...
// Получаем путь во временном каталоге сервера
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ИмяВременногоФайла, ТипФайлаТабличногоДокумента.XLSX);
// Читаем файл в двоичные данные и помещаем в хранилище
ДвоичныеДанные = Новый ДвоичныеДанные(ИмяВременногоФайла);
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанные, ЭтаФорма.УникальныйИдентификатор);
// Удаляем временный файл с диска сервера после прочтения
УдалитьФайлы(ИмяВременногоФайла);
Возврат АдресХранилища;
КонецФункции
Затем на клиенте вызываем получение этого файла. В новых версиях платформы для этого рекомендуется использовать асинхронные методы:
&НаКлиенте
Процедура Выгрузить(Команда)
Адрес = СформироватьФайлНаСервере();
Если ЗначениеЗаполнено(Адрес) Тогда
ПараметрыПолучения = Новый ПараметрыПолученияФайла;
ПараметрыПолучения.Заголовок = "Сохраните выгрузку Excel";
// Предлагаем пользователю выбрать путь сохранения на его компьютере
НачатьПолучениеФайлаССервера( , Адрес, "Выгрузка.xlsx", ПараметрыПолучения);
КонецЕсли;
КонецПроцедуры
Часто программисты пытаются передать саму ТаблицуЗначений с сервера на клиент, чтобы обработать её там, и сталкиваются с ошибкой «Ошибка отображения типов» или «Ошибка преобразования данных XDTO». Это происходит потому, что объект ТаблицаЗначений не существует на клиенте в тонком клиенте.
Выясним, как правильно передать данные для выгрузки:
ЭтаФорма).Массив и Структура являются универсальными и свободно перемещаются между контекстами.Посмотрим на пример преобразования ТаблицыЗначений в массив для передачи на клиент:
&НаСервере
Функция ПолучитьДанныеДляВыгрузки()
ТЗ = Новый ТаблицаЗначений;
// ... заполнение ТЗ данными ...
МассивРезультат = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
НоваяСтруктура = Новый Структура;
Для Каждого Колонка Из ТЗ.Колонки Цикл
НоваяСтруктура.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
КонецЦикла;
МассивРезультат.Добавить(НоваяСтруктура);
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
Если специфика задачи требует записи файла именно в определенную папку на сервере (например, для последующего забора файла другой системой), необходимо убедиться, что целевой каталог существует. Проанализируем, как добавить проверку и создание папки C:\Обмен\ перед записью файла:
&НаСервере
Процедура ЗаписатьФайлНаСервер(ТабДок, ПолноеИмяФайла)
// Выделяем путь к каталогу из полного имени файла
Файл = Новый Файл(ПолноеИмяФайла);
ПутьККаталогу = Файл.Путь;
// Проверяем наличие каталога и создаем его при необходимости
КаталогНаДиске = Новый Файл(ПутьККаталогу);
Если Не КаталогНаДиске.Существует() Тогда
СоздатьКаталог(ПутьККаталогу);
КонецЕсли;
// Теперь запись пройдет без ошибки "Путь не найден", если есть права
ТабДок.Записать(ПолноеИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
КонецПроцедуры
При использовании метода Записать() крайне важно правильно выбрать тип файла. В старом коде часто встречается ТипФайлаТабличногоДокумента.XLS97. Рассмотрим, почему стоит перейти на XLSX:
XLS (Excel 97-2003) ограничен 65 536 строками. Если ваша выгрузка проводок или документов окажется больше, программа выдаст ошибку или обрежет данные.XLSX использует сжатие XML, поэтому файлы занимают значительно меньше места на диске и быстрее передаются по сети.Записать() в 1С не требуется наличие установленного Microsoft Excel на сервере. 1С формирует эти файлы собственными средствами.Важное замечание: Если вы работаете через Веб-клиент (в браузере), прямая запись на диск C:\ программным кодом запрещена политиками безопасности браузеров. В этом случае использование временного хранилища и метода ПолучитьФайл() является единственным рабочим способом предоставить пользователю результат выгрузки.
Таким образом, для надежной работы выгрузки мы рекомендуем: формировать ТабличныйДокумент на сервере, использовать формат XLSX, помещать результат во временное хранилище и предлагать пользователю скачать его на клиентской стороне.