При разработке обработок выгрузки и отчетов в управляемом приложении 1С:Предприятие 8.3 часто возникает задача: сформировать файл (например, DBF, Excel или текстовый документ) на стороне сервера и сохранить его на локальный диск пользователя через тонкий клиент. При реализации этого сценария разработчики нередко сталкиваются с ошибкой «Отказано в доступе» при вызове процедуры ПолучитьФайлы или записи на стороне клиента.
Разберем подробно причины возникновения этой ошибки, разграничение прав между сервером и клиентом, а также правильные способы передачи и сохранения файлов.
Выясним причину, почему возникает ошибка доступа и как работают учетные записи в клиент-серверной архитектуре:
USR1CV8) имеет доступ только к ресурсам сервера и сети, разрешенным администратором. Однако при выполнении операций сохранения на стороне клиента серверный пользователь не участвует в записи файла на диск пользователя. Код, исполняемый в контексте &НаКлиенте, запускается с правами текущего пользователя операционной системы Windows на рабочем месте клиента.ОписаниеПередаваемогоФайла: Частая ошибка заключается в том, что в качестве имени файла передается путь к каталогу (например, C:\TEMP), а не полное имя файла с расширением (например, C:\TEMP\export.dbf). Попытка платформы записать данные в дескриптор каталога на уровне файловой системы ОС Windows интерпретируется именно как отказ в доступе.C:\ или каталоги Program Files, Windows), пользователь без прав администратора получит ошибку.Рассмотрим классический механизм платформы с передачей файла через временное хранилище и вызовом метода ПолучитьФайлы. Разберем по шагам правильное формирование структуры выгрузки:
ПолучитьИмяВременногоФайла.ДвоичныеДанные и функцию ПоместитьВоВременноеХранилище.ОписаниеПередаваемогоФайла, обязательно указывая полный путь, включая имя и расширение целевого файла.ПолучитьФайлы.Посмотрим на пример реализации на сервере и клиенте:
&НаСервере
Функция СформироватьФайлНаСервере()
// Получаем уникальное имя во временном каталоге сервера
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("dbf");
// Здесь выполняется логика формирования DBF или другого файла
// Например, работа с объектом XBase:
База = Новый XBase;
База.Поля.Добавить("NAME", "S", 50);
База.СоздатьФайл(ИмяВременногоФайла);
База.Добавить();
База.NAME = "Пример данных";
База.Записать();
База.ЗакрытьФайл();
// Считываем сформированный файл в двоичные данные и помещаем в хранилище
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, Новый УникальныйИдентификатор);
// Удаляем временный файл с сервера
УдалитьФайлы(ИмяВременногоФайла);
Возврат АдресХранилища;
КонецФункции
&НаКлиенте
Процедура СохранитьФайлЧерезОписание(Команда)
// Полный путь сохранения на клиентской машине
КаталогНаКлиенте = "C:\Users\Public\Documents";
ПолноеИмяФайлаНаКлиенте = КаталогНаКлиенте + "\export.dbf";
// Получаем адрес во временном хранилище
АдресФайлаВХранилище = СформироватьФайлНаСервере();
// Формируем описание файла: первый параметр - полный путь к файлу на клиенте, второй - адрес в хранилище
ОписаниеФайла = Новый ОписаниеПередаваемогоФайла;
ОписаниеФайла.Имя = ПолноеИмяФайлаНаКлиенте;
ОписаниеФайла.Хранение = АдресФайлаВХранилище;
МассивФайлов = Новый Массив;
МассивФайлов.Добавить(ОписаниеФайла);
// Выполняем получение файла на сторону клиента
ПолучитьФайлы(МассивФайлов, , , Ложь);
Сообщить("Файл успешно сохранен по пути: " + ПолноеИмяФайлаНаКлиенте);
КонецПроцедуры
Проанализируем альтернативный и более компактный способ, который часто удобнее использовать в управляемых формах. Мы можем вернуть объект ДвоичныеДанные напрямую из серверной функции и сохранить его на клиенте вызовом метода Записать.
Посмотрим на пример кода:
&НаСервере
Функция ПолучитьДвоичныеДанныеВыгрузкиНаСервере()
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("dbf");
// Формирование файла на сервере
База = Новый XBase;
База.Поля.Добавить("CODE", "S", 10);
База.СоздатьФайл(ИмяВременногоФайла);
База.Добавить();
База.CODE = "00001";
База.Записать();
База.ЗакрытьФайл();
Данные = Новый ДвоичныеДанные(ИмяВременногоФайла);
УдалитьФайлы(ИмяВременногоФайла);
Возврат Данные;
КонецФункции
&НаКлиенте
Процедура СохранитьНапрямую(Команда)
// Получаем двоичные данные с сервера
ФайлДвоичныеДанные = ПолучитьДвоичныеДанныеВыгрузкиНаСервере();
ПутьДляСохранения = "C:\Users\Public\Documents\result.dbf";
// Записываем файл на диск клиента
ФайлДвоичныеДанные.Записать(ПутьДляСохранения);
Сообщить("Файл записан: " + ПутьДляСохранения);
КонецПроцедуры
Чтобы избежать ошибок с правами доступа к жестко заданным путям, рекомендуется предоставить пользователю возможность самостоятельно выбрать каталог и имя для сохранения файла через ДиалогВыбораФайла.
Разберем пример с интерактивным сохранением:
&НаКлиенте
Процедура ВыгрузитьСВыборомПути(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
Диалог.Заголовок = "Выберите путь для сохранения файла";
Диалог.Фильтр = "Файлы DBF (*.dbf)|*.dbf|Все файлы (*.*)|*.*";
Диалог.ПолноеИмяФайла = "Выгрузка.dbf";
Если Диалог.Выбрать() Тогда
ВыбранныйПуть = Диалог.ПолноеИмяФайла;
ДвоичныеДанныеВыгрузки = ПолучитьДвоичныеДанныеВыгрузкиНаСервере();
ДвоичныеДанныеВыгрузки.Записать(ВыбранныйПуть);
ПоказатьОповещениеПользователя("Успешно", , "Файл сохранен: " + ВыбранныйПуть);
КонецЕсли;
КонецПроцедуры
СоздатьКаталог.ПолучитьИмяВременногоФайла вместо фиксированных путей вроде C:\TEMP, так как на сервере может отсутствовать данный каталог или к нему могут быть ограничены права.НачатьПолучениеФайлов, ПоказатьДиалогВыбора или ДвоичныеДанные.ЗаписатьАсинх).ПолучитьФайл), чтобы не создавать конфликтов контекста и неоднозначности в коде.