При работе в управляемом приложении 1С начинающие разработчики часто сталкиваются с ситуацией, когда код, успешно работающий в файловой базе или на клиенте, выдает ошибку при выполнении на сервере. Типичный пример — запись XML-файла. Попытка сохранить файл по пути C:\Users\ИмяПользователя\Desktop\doc.xml приводит к системному сообщению: «Ошибка при вызове метода контекста (ОткрытьФайл): Каталог не обнаружен». Проанализируем, почему это происходит, и разберем правильные алгоритмы работы с файлами в контексте клиент-серверной архитектуры.
Разберем ситуацию: когда мы запускаем код на сервере (например, в процедуре с директивой &НаСервере), выполнение происходит от имени системного пользователя, под которым запущена служба 1C:Enterprise Server (обычно это USR1CV8). Рассмотрим основные причины возникновения ошибки:
C:\Users\... будет воспринят как некорректный, так как в Linux используется иная структура путей.C: — это локальный диск самого сервера, а не того компьютера, за которым сидит бухгалтер.Для решения этой задачи мы рассмотрим три надежных способа: использование временных файлов, работу с памятью (строками) и правильную передачу данных между сервером и клиентом.
Самый правильный способ работы с файлами на сервере — использование специально отведенных системных папок. Функция ПолучитьИмяВременногоФайла() гарантирует, что путь будет валидным и у службы 1С будут права на запись в этот каталог.
Проанализируем пример кода для записи XML во временный файл:
&НаСервере
Функция СформироватьXMLНаСервере()
// Получаем путь во временном каталоге сервера
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML = Новый ЗаписьXML;
// Параметры записи помогут сделать файл читаемым (с отступами)
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
Попытка
ЗаписьXML.ОткрытьФайл(ИмяФайла, ПараметрыЗаписи);
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("RootFolder");
// Здесь логика заполнения данными
ЗаписьXML.ЗаписатьТекст("Данные для выгрузки");
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть();
Исключение
ЗаписьXML.Закрыть();
ВызватьИсключение "Ошибка при записи XML: " + ОписаниеОшибки();
КонецПопытки;
Возврат ИмяФайла;
КонецФункции
Если объем данных небольшой, мы можем вообще избежать обращения к файловой системе сервера. Это повышает производительность и избавляет от проблем с правами доступа. Вместо метода ОткрытьФайл() используем УстановитьСтроку().
Рассмотрим, как это реализовать:
&НаСервере
Функция ПолучитьТекстXML()
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8"); // Формируем данные в оперативной памяти
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("DataExport");
// Формируем структуру XML
ЗаписьXML.ЗаписатьАтрибут("ДатаСоздания", Строка(ТекущаяДата()));
ЗаписьXML.ЗаписатьКонецЭлемента();
РезультатСтрокой = ЗаписьXML.Закрыть();
Возврат РезультатСтрокой;
КонецФункции
После получения строки на клиенте, её можно легко сохранить в файл, используя объект ТекстовыйДокумент или ЗаписьТекста, уже имея доступ к локальным папкам пользователя.
В управляемом приложении это «золотой стандарт». Мы создаем файл на сервере (как в первом способе), помещаем его во временное хранилище 1С, передаем адрес этого хранилища на клиент и там предлагаем пользователю сохранить файл в любое удобное место.
Разберем по шагам реализацию этой схемы:
&НаКлиенте
Процедура ВыгрузитьXML(Команда)
// 1. Формируем файл на сервере и получаем адрес в хранилище
АдресФайла = СформироватьИПоместитьВоХранилище();
// 2. Определяем имя файла, которое увидит пользователь
ИмяВыгрузки = "Выгрузка_Данных.xml";
// 3. Получаем файл из хранилища на локальный компьютер
ПолучитьФайл(АдресФайла, ИмяВыгрузки, Истина);
КонецПроцедуры
&НаСервере
Функция СформироватьИПоместитьВоХранилище()
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
// Код записи XML (аналогично Способу 1)
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла);
ЗаписьXML.ЗаписатьНачалоЭлемента("Root");
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть();
// Преобразуем файл в двоичные данные для помещения в хранилище
ДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
Адрес = ПоместитьВоВременноеХранилище(ДанныеФайла, ЭтаФорма.УникальныйИдентификатор);
// Удаляем временный файл на сервере, он больше не нужен
УдалитьФайлы(ИмяВременногоФайла);
Возврат Адрес;
КонецФункции
Чтобы ваш XML-файл был корректным и легко читался другими системами, придерживайтесь следующих правил:
UTF-8) и устанавливайте отступы. Это поможет при отладке — вы сможете открыть файл в любом текстовом редакторе и увидеть структуру, а не одну бесконечную строку.ЗаписьXML.Закрыть() обязателен. Если его не вызвать, файл останется занятым процессом сервера, и вы не сможете его прочитать, переместить или удалить.Попытка...Исключение.ПолучитьИмяВременногоФайла(), не забудьте удалить его после завершения операции, чтобы не «мусорить» в системных папках сервера.Таким образом, для исправления ошибки «Каталог не обнаружен» достаточно перенести логику формирования пути на системные механизмы 1С и обеспечить корректную передачу данных между контекстами исполнения программы.