При выгрузке документов или справочников из 1С в файл XML разработчики часто сталкиваются с ситуацией, когда на локальной файловой базе код работает корректно, а в клиент-серверном режиме платформы вылетает ошибка вида: «Ошибка при вызове метода контекста (ОткрытьФайл)... Ошибка при выполнении файловой операции».
Проанализируем причины возникновения этой проблемы и разберем проверенные способы ее решения.
Рассмотрим типовой сценарий, в котором проявляется ошибка. На стороне клиента открывается интерактивный диалог выбора файла (объект ДиалогВыбораФайла). Пользователь выбирает путь на своем локальном компьютере или в сетевой папке (например, C:\Users\User\Desktop\File.xml или \\Server\Share\File.xml). Затем выбранный путь передается в серверную процедуру с директивой &НаСервере, где выполняется метод ЗаписьXML.ОткрытьФайл().
В этот момент происходят следующие критические сбои:
C: клиентского компьютера.\\Server\Share\), служба 1C:Enterprise Server (например, пользователь usr1cv8 или системная учетная запись Local System) часто не имеет прав на чтение и запись в эту сетевую папку.Наиболее оптимальный и универсальный способ — разделить формирование данных и файловую операцию. На сервере сформируем структуру XML прямо в оперативной памяти в виде строки, вернем эту строку на клиент и уже на клиентском компьютере запишем её в файл.
Разберем реализацию этого способа на примере:
&НаКлиенте
Процедура ВыгрузитьXML(Команда)
ДиалогXML = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
ДиалогXML.ПолноеИмяФайла = "Перемещение_Товаров_" + СокрЛП(Строка(Объект.Номер)) + "_" + Формат(Объект.Дата, "ДФ=yyyyMMdd");
ДиалогXML.Фильтр = "Файлы XML (*.xml)|*.xml";
Если ДиалогXML.Выбрать() Тогда
ИмяФайла = ДиалогXML.ПолноеИмяФайла;
// Получаем содержимое XML с сервера в виде строки
ТекстXML = ВыгрузитьXMLНаСервере(Объект.Ссылка);
// Записываем файл на стороне клиента
ТекстовыйФайл = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
ТекстовыйФайл.Записать(ТекстXML);
ТекстовыйФайл.Закрыть();
ПоказатьОповещениеПользователя("Успех", , "Файл успешно выгружен!");
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ВыгрузитьXMLНаСервере(СсылкаДокумент)
XML = Новый ЗаписьXML;
// Настраиваем запись XML в строку в кодировке UTF-8
XML.УстановитьСтроку("UTF-8");
XML.ЗаписатьОбъявлениеXML();
XML.ЗаписатьНачалоЭлемента("ExportedAccDocData");
XML.ЗаписатьАтрибут("xmlns", "http://www.site.ru/namespace");
// Заполнение структуры XML данными
Обр = Обработки.СозданиеФайлаXMLДляПеремещения.Создать();
Обр.Выгрузить(XML, СсылкаДокумент);
XML.ЗаписатьКонецЭлемента();
// Метод Закрыть() возвращает итоговую строку XML
Возврат XML.Закрыть();
КонецФункции
В данном примере метод XML.УстановитьСтроку() позволяет избежать любых файловых операций на сервере, исключая ошибки доступа.
Если файл выгрузки имеет большой объем и формирование его целиком в оперативной памяти нежелательно, следует использовать безопасный временный каталог сервера и механизм работы с временным хранилищем 1С.
Рассмотрим пошагово этот алгоритм:
ПолучитьИмяВременногоФайла("xml").ДвоичныеДанные во временное хранилище через метод ПоместитьВоВременноеХранилище().УдалитьФайлы().
&НаКлиенте
Процедура ВыгрузитьЧерезХранилище(Команда)
ДиалогXML = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
ДиалогXML.Фильтр = "Файлы XML (*.xml)|*.xml";
Если ДиалогXML.Выбрать() Тогда
АдресХранилища = СформироватьФайлВХранилищеНаСервере(Объект.Ссылка);
// Получаем двоичные данные из хранилища и сохраняем на клиентском диске
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
ДвоичныеДанные.Записать(ДиалогXML.ПолноеИмяФайла);
ОчиститьВременноеХранилище(АдресХранилища);
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция СформироватьФайлВХранилищеНаСервере(СсылкаДокумент)
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
XML = Новый ЗаписьXML;
XML.ОткрытьФайл(ИмяВременногоФайла, "UTF-8");
XML.ЗаписатьОбъявлениеXML();
XML.ЗаписатьНачалоЭлемента("Data");
// Формирование структуры XML...
XML.ЗаписатьКонецЭлемента();
XML.Закрыть();
// Переводим файл в двоичные данные и помещаем в хранилище
ДвоичныеДанные = Новый ДвоичныеДанные(ИмяВременногоФайла);
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанные, УникальныйИдентификатор);
// Обязательно удаляем временный файл на сервере
УдалитьФайлы(ИмяВременногоФайла);
Возврат АдресХранилища;
КонецФункции
Если архитектура решения требует, чтобы файл генерировался непосредственно сервером 1С в определенный общий сетевой ресурс (например, в папку обмена \\ServerName\Exchange\), необходимо выдать соответствующие права службе 1С.
Выполним следующие шаги настройки:
services.msc) на сервере приложений 1С.Агент сервера 1С:Предприятие 8.3.DOMAIN\1c_service).После этого метод XML.ОткрытьФайл() сможет успешно записывать данные по UNC-путям вида \\ServerName\Exchange\file.xml напрямую из серверного контекста.