Разработчики 1С часто сталкиваются с ситуацией, когда код, без проблем работающий в интерактивном режиме на клиенте, внезапно аварийно завершается при переносе его выполнения на сервер — например, в фоновом или регламентном задании. Типичный сценарий: подписка на событие или фоновый процесс формирует печатную форму (например, платежное поручение), пытается выгрузить её в табличный документ и записать во внешний файл формата PDF, но в ответ возвращает системную ошибку:
«Ошибка при вызове метода контекста (Записать): Ошибка при выполнении файловой операции»
В этой статье мы подробно разберем, почему возникает данная проблема, проанализируем неочевидные тонкости работы серверных процессов с файловой системой и рассмотрим пошаговые инструкции по исправлению этой ошибки.
---
Когда вы запускаете аналогичный код вручную на клиенте (под своей учетной записью), у вас развернуто полноценное окружение операционной системы, настроены все пути доступа к временным каталогам и есть полные права на интерактивные действия. В фоновом задании контекст принципиально иной. Выделим основные технические причины сбоя:
"Платежное поручение № 31189...pdf"). Операционная система заблокирует файл для второго процесса, и 1С выдаст ошибку файловой операции.
КаталогВременныхФайлов() + ИмяФайла платформа обращается к системной папке Temp пользователя, под которым запущена служба сервера 1С (например, usr1cv8 или s-1cserviceuser). После обновлений Windows или перезагрузок серверов временная папка профиля службы может быть удалена системой. Метод простого сложения путей не умеет автоматически создавать недостающие директории, в отличие от платформенных механизмов генерации путей.
ИспользованиеВывода у объекта ТабличныйДокумент по умолчанию может принимать значение Запретить. Это блокирует любую попытку записи или печати документа.
---
Рассмотрим комплекс мер, которые гарантируют стабильное сохранение файлов из фоновых заданий без ошибок.
Главная ошибка многих разработчиков — ручное конструирование путей файлов во временных каталогах. Избавимся от этого. Метод ПолучитьИмяВременногоФайла("pdf") решает сразу несколько проблем:
Проанализируем пример некорректного кода:
// НЕКОРРЕКТНО:
ИмяФайла = "Платежное поручение № " + НомерДокумента + ".pdf";
ПолныйПуть = КаталогВременныхФайлов() + ИмяФайла;
ТабличныйДокумент.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.PDF);
А теперь перепишем его правильно:
// КОРРЕКТНО:
ПутьКВременномуФайлу = ПолучитьИмяВременногоФайла("pdf");
ТабличныйДокумент.Записать(ПутьКВременномуФайлу, ТипФайлаТабличногоДокумента.PDF);
---
Перед записью табличного документа на сервере всегда явно указывайте разрешение на его вывод. Для этого используем системное перечисление ИспользованиеВывода. Добавим соответствующую строку кода:
ТабличныйДокумент.ИспользованиеВывода = ИспользованиеВывода.Разрешить;
Без этой строки на некоторых конфигурациях и при определенных настройках прав пользователей фоновое задание будет молча падать при попытке экспорта.
---
Если вам обязательно нужно сохранить файл именно с красивым «человекочитаемым» именем на диске, необходимо очистить его имя от потенциально опасных символов. В составе Библиотеки Стандартных Подсистем (БСП) для этого есть готовый инструмент. Рассмотрим пример очистки имени файла:
ИмяФайлаБезРасширения = "Платежное поручение № " + НомерДокумента + " от " + Формат(ДатаДокумента, "ДФ=dd.MM.yyyy");
// Очищаем от спецсимволов средствами БСП
ИмяФайлаБезРасширения = ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(ИмяФайлаБезРасширения, "-");
// Чтобы избежать конфликта путей, создаем подкаталог с уникальным GUID
УникальныйСубкаталог = ПолучитьИмяВременногоФайла("");
СоздатьКаталог(УникальныйСубкаталог);
ПолныйПуть = УникальныйСубкаталог + "\" + ИмяФайлаБезРасширения + ".pdf";
ТабличныйДокумент.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.PDF);
---
Если конечная цель фонового задания — отдать сформированный PDF-файл пользователю в сеанс, то самым надежным и архитектурно правильным решением будет отказ от постоянного хранения файлов на диске сервера. Мы запишем файл во временный путь, немедленно поместим его в базу данных во временное хранилище, а физический файл удалим.
Проанализируем комплексный пример реализации этого паттерна:
Функция СформироватьPDFВоВременномХранилище(ТабличныйДокумент) Экспорт
// Разрешаем вывод
ТабличныйДокумент.ИспользованиеВывода = ИспользованиеВывода.Разрешить;
// Получаем уникальный временный путь
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
Попытка
// Записываем файл на диск сервера
ТабличныйДокумент.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);
// Помещаем двоичные данные во временное хранилище
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);
// Сразу удаляем временный файл, чтобы не засорять диск сервера
УдалитьФайлы(ИмяФайла);
Возврат АдресХранилища;
Исключение
// В случае ошибки обязательно пытаемся очистить файл и пишем лог в ЖР
Если ФайлСуществует(ИмяФайла) Тогда
УдалитьФайлы(ИмяФайла);
КонецЕсли;
ЗаписьЖурналаРегистрации("ВыгрузкаВPDF",
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение "Не удалось сохранить печатную форму в PDF. Обратитесь к администратору.";
КонецПопытки;
КонецФункции
Функция ФайлСуществует(ПутьКФайлу)
ФайлНаДиске = Новый Файл(ПутьКФайлу);
Возврат ФайлНаДиске.Существует();
КонецФункции
После этого на стороне клиента вы легко сможете получить этот файл по возвращенному адресу временного хранилища и предложить пользователю сохранить его на локальный компьютер с любым удобным именем:
&НаКлиенте
Процедура ПолучитьФайлССервера(АдресХранилища, КрасивоеИмяФайла)
ПараметрыПолучения = Новый ПараметрыПолученияФайлаССервера;
ПараметрыПолучения.ИмяФайлаПредопределено = Истина;
ПолучитьФайлССервера(АдресХранилища, КрасивоеИмяФайла, ПараметрыПолучения);
КонецПроцедуры
---
Если вышеописанные изменения кода не помогли, локализуйте проблему на уровне операционной системы:
1C:Enterprise Server. Зайдите на сервер под этой учетной записью и проверьте физическое существование каталога C:\Users\%ИмяПользователя%\AppData\Local\Temp. Запишите в эту папку любой тестовый файл вручную.
services.msc) на сервере приложений 1С и перезапустите службу «Диспетчер печати» (Print Spooler). Убедитесь, что тип ее запуска установлен в «Автоматически».
.pdf процессами rphost.exe. Добавьте рабочие процессы 1С в исключения антивирусного монитора.
Применяя данные подходы, вы навсегда избавитесь от нестабильного поведения фоновых заданий при формировании файлов отчетов и печатных форм.