При работе в облачных инфраструктурах и терминальных средах (RDP) разработчики и пользователи часто сталкиваются с неприятной ошибкой. При попытке сохранить сформированный отчет или документ (например, в формате Excel) программа выдает системное сообщение: «Отказано в доступе» или «Сетевой путь не найден». При этом ручная выгрузка файлов через стандартные функции платформы или копирование файлов через проводник в той же сессии проходят без каких-либо затруднений.
В этой статье мы подробно разберем, почему возникает эта ошибка с технической точки зрения, проанализируем разницу контекстов выполнения кода в 1С:Предприятии и рассмотрим три надежных способа решения данной проблемы с готовыми примерами кода.
Для того чтобы понять суть проблемы, проанализируем, в каких окружениях выполняются клиентская и серверная части 1С при работе через удаленный рабочий стол.
Когда пользователь подключается к удаленному серверу (облаку) по протоколу RDP и ставит в настройках галочку «Локальные ресурсы -> Диски», операционная система терминального сервера создает виртуальные каналы для связи. Локальные диски вашего физического компьютера монтируются внутри удаленной сессии Windows под динамическими путями вида \\tsclient\C, \\tsclient\D или аналогичными сетевыми адресами, сгенерированными сторонним софтом для подключения.
Важно понимать ключевое различие в правах доступа:
1C:Enterprise 8.3 Server Agent, процессы rphost.exe) функционирует в изолированной сервисной сессии операционной системы (так называемая «Сессия 0»). Эта служба запускается под специальной локальной учетной записью (например, usr1cv8). Сервер 1С физически ничего не знает о существовании вашей личной RDP-сессии, ваших проброшенных дисках и путях типа \\tsclient\.... Для операционной системы попытка процесса 1С обратиться к динамическому сетевому диску конкретного пользователя выглядит как нарушение безопасности, что и приводит к ошибке «Отказано в доступе».Таким образом, выполнение метода Записать() для табличного документа на сервере по пути, который принадлежит сессии RDP-клиента, принципиально невозможно без изменения архитектуры вызова.
Рассмотрим наиболее правильный и простой с точки зрения стандартов разработки 1С метод решения. Поскольку объект ТабличныйДокумент является сериализуемым, мы можем без проблем передавать его между сервером и клиентом.
Разберем этот алгоритм по шагам:
ТабличныйДокумент обратно на клиент.Записать().Посмотрим на пример программной реализации этого подхода:
&НаКлиенте
Процедура СохранитьТаблицуВЭксель(ИмяФайла)
// Шаг 1. Получаем полностью сформированный документ с сервера
ТабДок = СформироватьТабличныйДокументНаСервере();
// Шаг 2. Записываем файл на клиенте, используя доступ к RDP-ресурсам
Попытка
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
ПоказатьПредупреждение(, "Файл успешно сохранен по пути: " + ИмяФайла);
Исключение
// Обрабатываем возможные ошибки записи на стороне операционной системы клиента
ВызватьИсключение "Не удалось записать файл. Проверьте права доступа. Описание: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
&НаСервере
Функция СформироватьТабличныйДокументНаСервере()
ТабДок = Новый ТабличныйДокумент;
// Получаем данные для выгрузки (в данном примере из реквизита формы)
ТЗ = ТаблицаЗначенийОтчет.Выгрузить();
// Используем построитель отчета для быстрого заполнения табличного документа
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ);
Построитель.ВыводитьЗаголовокОтчета = Ложь;
Построитель.Вывести(ТабДок);
// Возвращаем объект на клиентскую сторону
Возврат ТабДок;
КонецФункции
Этот способ идеален для выгрузки отчетов, печатных форм и любых документов, которые изначально формируются в виде табличного документа 1С.
Что делать, если на сервере формируется не просто табличный документ, а бинарный файл сторонними средствами (например, архивируется ZIP-архив, генерируется PDF сторонней библиотекой или выгружается XML/JSON-структура через специализированные компоненты)? В таком случае передать сам объект напрямую на клиент не получится.
Проанализируем универсальную схему решения через Временное хранилище:
rphost.exe гарантированно есть права на запись, например, через ПолучитьИмяВременногоФайла()).ДвоичныеДанные.Рассмотрим пример кода, реализующий эту схему:
&НаКлиенте
Процедура ЭкспортироватьБинарныйФайл(ИмяФайлаНаКлиенте)
// Шаг 1. Запускаем серверную генерацию и получаем адрес во временном хранилище
АдресХранилища = СформироватьБинарныйФайлНаСервере();
Если ЗначениеЗаполнено(АдресХранилища) Тогда
// Шаг 2. Настраиваем параметры сохранения файла на клиенте
ПараметрыПолучения = Новый ПараметрыПолученияФайлаИзВременногоХранилища;
ПараметрыПолучения.Интерактивно = Ложь; // Сохраняем программно без диалоговых окон
// Шаг 3. Записываем файл на диск клиента
ПолучитьФайлИзВременногоХранилища(АдресХранилища, ИмяФайлаНаКлиенте, ПараметрыПолучения);
ПоказатьПредупреждение(, "Файл успешно передан и сохранен!");
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция СформироватьБинарныйФайлНаСервере()
// Получаем безопасный путь во временном каталоге сервера 1С
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
// Генерация файла (для примера используем запись пустого табличного документа)
ТабДок = Новый ТабличныйДокумент;
ТабДок.Записать(ИмяВременногоФайла, ТипФайлаТабличногоДокумента.XLSX);
// Читаем созданный файл в двоичные данные
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
// Помещаем данные в хранилище, привязывая к уникальному идентификатору формы
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, ЭтаФорма.УникальныйИдентификатор);
// Обязательно удаляем временный файл на сервере, чтобы не засорять диск
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Логируем ошибку удаления, если это необходимо, но не прерываем процесс
КонецПопытки;
Возврат АдресХранилища;
EndFunction
Использование связки ПоместитьВоВременноеХранилище и ПолучитьФайлИзВременногоХранилища полностью снимает любые проблемы с правами доступа операционной системы, так как передача информации происходит по внутренним каналам платформы 1С.
В некоторых случаях бизнес-процесс требует автоматической выгрузки файлов без участия пользователя (например, при работе регламентных или фоновых заданий, у которых вообще нет клиентского контекста). В этой ситуации перенаправление RDP-клиента использовать невозможно.
Рассмотрим, как правильно настроить серверную среду для таких задач:
\\tsclient\D согласуйте с системным администратором выделение постоянного сетевого ресурса (например, \\Fileserver\Exchange\).1C:Enterprise 8.3 Server Agent и посмотрите, от имени какого пользователя она запускается. Обычно это локальный пользователь usr1cv8.Проанализируем важный нюанс: при написании серверного кода для работы с сетевыми путями всегда избегайте обращения к дискам по буквам (например, W:\Folder\file.txt). Буквы дисков монтируются в контексте сеанса конкретного пользователя. Серверная служба 1С этих букв не увидит. Используйте исключительно полные UNC-пути вида \\имя_сервера\имя_сетевой_папки\имя_файла.
Мы подробно разобрали техническую природу ошибки доступа к проброшенным дискам в облачной и терминальной инфраструктуре. Главный вывод: никогда не пытайтесь записывать файлы напрямую на клиентские RDP-ресурсы из серверных процедур.
Используйте архитектурно верное разделение: выполняйте тяжелые расчеты и сборку документов в контексте &НаСервере, а сохранение готового результата на физические диски пользователей делегируйте контексту &НаКлиенте. Это убережет ваши решения от сбоев при любых изменениях в настройках терминальных подключений и обновлении сетевой инфраструктуры.