Как правильно сохранить сформированный на сервере файл на компьютер пользователя в тонком клиенте 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке обработок выгрузки и отчетов в управляемом приложении 1С:Предприятие 8.3 часто возникает задача: сформировать файл (например, DBF, Excel или текстовый документ) на стороне сервера и сохранить его на локальный диск пользователя через тонкий клиент. При реализации этого сценария разработчики нередко сталкиваются с ошибкой «Отказано в доступе» при вызове процедуры ПолучитьФайлы или записи на стороне клиента.

Разберем подробно причины возникновения этой ошибки, разграничение прав между сервером и клиентом, а также правильные способы передачи и сохранения файлов.

Анализ причин ошибки и контекстов выполнения

Выясним причину, почему возникает ошибка доступа и как работают учетные записи в клиент-серверной архитектуре:

  1. Разделение контекстов прав доступа: Серверная служба 1С (работающая под пользователем USR1CV8) имеет доступ только к ресурсам сервера и сети, разрешенным администратором. Однако при выполнении операций сохранения на стороне клиента серверный пользователь не участвует в записи файла на диск пользователя. Код, исполняемый в контексте &НаКлиенте, запускается с правами текущего пользователя операционной системы Windows на рабочем месте клиента.
  2. Некорректная передача параметров в ОписаниеПередаваемогоФайла: Частая ошибка заключается в том, что в качестве имени файла передается путь к каталогу (например, C:\TEMP), а не полное имя файла с расширением (например, C:\TEMP\export.dbf). Попытка платформы записать данные в дескриптор каталога на уровне файловой системы ОС Windows интерпретируется именно как отказ в доступе.
  3. Ограничения прав ОС на клиенте: Если каталог на клиенте защищен контролем учетных записей (UAC) или требует повышенных привилегий (например, запись в корень диска C:\ или каталоги Program Files, Windows), пользователь без прав администратора получит ошибку.
  4. Блокировка существующего файла: Если файл с таким именем уже существует и открыт в другой программе (например, DBF-файл открыт в стороннем просмотрщике или табличном редакторе), ОС заблокирует перезапись.

Способ 1. Использование временного хранилища и объекта ОписаниеПередаваемогоФайла

Рассмотрим классический механизм платформы с передачей файла через временное хранилище и вызовом метода ПолучитьФайлы. Разберем по шагам правильное формирование структуры выгрузки:

  1. На стороне сервера формируем файл во временном каталоге с помощью ПолучитьИмяВременногоФайла.
  2. Помещаем файл во временное хранилище через объект ДвоичныеДанные и функцию ПоместитьВоВременноеХранилище.
  3. На стороне клиента создаем объект ОписаниеПередаваемогоФайла, обязательно указывая полный путь, включая имя и расширение целевого файла.
  4. Вызываем метод ПолучитьФайлы.

Посмотрим на пример реализации на сервере и клиенте:


&НаСервере
Функция СформироватьФайлНаСервере()
    
    // Получаем уникальное имя во временном каталоге сервера
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("dbf");
    
    // Здесь выполняется логика формирования DBF или другого файла
    // Например, работа с объектом XBase:
    База = Новый XBase;
    База.Поля.Добавить("NAME", "S", 50);
    База.СоздатьФайл(ИмяВременногоФайла);
    База.Добавить();
    База.NAME = "Пример данных";
    База.Записать();
    База.ЗакрытьФайл();
    
    // Считываем сформированный файл в двоичные данные и помещаем в хранилище
    ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
    АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, Новый УникальныйИдентификатор);
    
    // Удаляем временный файл с сервера
    УдалитьФайлы(ИмяВременногоФайла);
    
    Возврат АдресХранилища;

КонецФункции

&НаКлиенте
Процедура СохранитьФайлЧерезОписание(Команда)
    
    // Полный путь сохранения на клиентской машине
    КаталогНаКлиенте = "C:\Users\Public\Documents";
    ПолноеИмяФайлаНаКлиенте = КаталогНаКлиенте + "\export.dbf";
    
    // Получаем адрес во временном хранилище
    АдресФайлаВХранилище = СформироватьФайлНаСервере();
    
    // Формируем описание файла: первый параметр - полный путь к файлу на клиенте, второй - адрес в хранилище
    ОписаниеФайла = Новый ОписаниеПередаваемогоФайла;
    ОписаниеФайла.Имя = ПолноеИмяФайлаНаКлиенте;
    ОписаниеФайла.Хранение = АдресФайлаВХранилище;
    
    МассивФайлов = Новый Массив;
    МассивФайлов.Добавить(ОписаниеФайла);
    
    // Выполняем получение файла на сторону клиента
    ПолучитьФайлы(МассивФайлов, , , Ложь);
    
    Сообщить("Файл успешно сохранен по пути: " + ПолноеИмяФайлаНаКлиенте);

КонецПроцедуры

Способ 2. Прямая передача ДвоичныхДанных и метод Записать()

Проанализируем альтернативный и более компактный способ, который часто удобнее использовать в управляемых формах. Мы можем вернуть объект ДвоичныеДанные напрямую из серверной функции и сохранить его на клиенте вызовом метода Записать.

Посмотрим на пример кода:


&НаСервере
Функция ПолучитьДвоичныеДанныеВыгрузкиНаСервере()
    
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("dbf");
    
    // Формирование файла на сервере
    База = Новый XBase;
    База.Поля.Добавить("CODE", "S", 10);
    База.СоздатьФайл(ИмяВременногоФайла);
    База.Добавить();
    База.CODE = "00001";
    База.Записать();
    База.ЗакрытьФайл();
    
    Данные = Новый ДвоичныеДанные(ИмяВременногоФайла);
    УдалитьФайлы(ИмяВременногоФайла);
    
    Возврат Данные;

КонецФункции

&НаКлиенте
Процедура СохранитьНапрямую(Команда)
    
    // Получаем двоичные данные с сервера
    ФайлДвоичныеДанные = ПолучитьДвоичныеДанныеВыгрузкиНаСервере();
    
    ПутьДляСохранения = "C:\Users\Public\Documents\result.dbf";
    
    // Записываем файл на диск клиента
    ФайлДвоичныеДанные.Записать(ПутьДляСохранения);
    
    Сообщить("Файл записан: " + ПутьДляСохранения);

КонецПроцедуры

Способ 3. Интерактивный выбор пути пользователем через ДиалогВыбораФайла

Чтобы избежать ошибок с правами доступа к жестко заданным путям, рекомендуется предоставить пользователю возможность самостоятельно выбрать каталог и имя для сохранения файла через ДиалогВыбораФайла.

Разберем пример с интерактивным сохранением:


&НаКлиенте
Процедура ВыгрузитьСВыборомПути(Команда)
    
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
    Диалог.Заголовок = "Выберите путь для сохранения файла";
    Диалог.Фильтр = "Файлы DBF (*.dbf)|*.dbf|Все файлы (*.*)|*.*";
    Диалог.ПолноеИмяФайла = "Выгрузка.dbf";
    
    Если Диалог.Выбрать() Тогда
        ВыбранныйПуть = Диалог.ПолноеИмяФайла;
        
        ДвоичныеДанныеВыгрузки = ПолучитьДвоичныеДанныеВыгрузкиНаСервере();
        ДвоичныеДанныеВыгрузки.Записать(ВыбранныйПуть);
        
        ПоказатьОповещениеПользователя("Успешно", , "Файл сохранен: " + ВыбранныйПуть);
    КонецЕсли;

КонецПроцедуры

Рекомендации по надежной работе с файлами

  1. Проверка существования каталога: Перед записью файла на клиенте убедитесь, что целевой каталог существует. Если целевая папка отсутствует, ее необходимо предварительно создать с помощью метода СоздатьКаталог.
  2. Использование временных папок: На стороне сервера всегда используйте функцию ПолучитьИмяВременногоФайла вместо фиксированных путей вроде C:\TEMP, так как на сервере может отсутствовать данный каталог или к нему могут быть ограничены права.
  3. Отказ от модальности: В современных конфигурациях при работе в режиме без использования модальности следует использовать асинхронные методы (например, НачатьПолучениеФайлов, ПоказатьДиалогВыбора или ДвоичныеДанные.ЗаписатьАсинх).
  4. Корректные имена функций: Избегайте переопределения встроенных функций платформы (например, не следует называть собственные функции как ПолучитьФайл), чтобы не создавать конфликтов контекста и неоднозначности в коде.
← На главную