В практике разработки на платформе 1С:Предприятие мы часто сталкиваемся с задачами интеграции, когда необходимо выгрузить файл (например, выполнить выгрузку движений документа в XML, сформировать файл Excel или текстовый лог) в определенную папку. Казалось бы, задача тривиальная, особенно если использовать готовые инструменты для копирования файлов и каталогов между клиентом и сервером. Однако, как только мы переходим от файлового варианта работы к клиент-серверному, и особенно когда целевая папка находится на другом сетевом ресурсе, возникают ошибки доступа. В этой статье мы подробно разберем, почему возникают такие проблемы, почему стандартные методы 1С не помогают, и как обойти ограничения, когда системные администраторы отказываются выдавать прямые права.
Прежде чем искать решение, давайте разберемся в архитектуре процесса. Когда мы работаем в управляемых формах и выполняем код на сервере (в директивах компиляции &НаСервере, &НаСервереБезКонтекста), этот код выполняется процессом сервера приложений 1С — обычно это rphost.exe. Это критично, когда требуется, например, экспорт документов базы в JSON для интеграции с внешними системами.
Операционная система Windows (или Linux) воспринимает этот процесс не как "пользователя Ивана, запустившего 1С", а как пользователя операционной системы, под которым запущена служба "Агент сервера 1С:Предприятия". Чаще всего это локальный служебный пользователь USR1CV8 или системная учетная запись Local System.
Проблема возникает в двух случаях:
SERVER1\USR1CV8). Когда этот пользователь пытается обратиться к сетевому пути \\FILESERVER\Exchange, удаленный сервер его не знает, так как этот пользователь существует только локально. Доступ блокируется.Многие разработчики ошибочно полагают, что использование конструкции УстановитьПривилегированныйРежим(Истина) решит проблему доступа к файлам. Давайте развеем этот миф.
Привилегированный режим в 1С отключает проверку:
Однако этот режим никак не влияет на права операционной системы. Код 1С не может приказать Windows игнорировать настройки безопасности файловой системы. Если у пользователя ОС, под которым запущен rphost.exe, нет прав на запись в папку, 1С получит отказ в доступе ("Access denied"), даже если включен привилегированный режим.
Если администраторы отказываются давать права пользователю, под которым работает сервер 1С (или это технически невозможно из-за отсутствия пользователя в домене), нам придется авторизоваться на сетевом ресурсе "на лету" прямо из кода 1С. Это часто необходимо для подготовки данных и последующего импорта данных из форматов XLS, XLSX, TXT или CSV в другие системы.
Для этого мы воспользуемся COM-объектом WScript.Network, который позволяет подключить сетевой диск или просто авторизоваться на ресурсе, используя переданные логин и пароль.
Рассмотрим алгоритм действий:
Посмотрим на пример реализации функции записи:
&НаСервере
Процедура ЗаписатьФайлВЗащищеннуюСетевуюПапку()
ПутьКРесурсу = "\\Server\HiddenShare";
Логин = "Domain\UserWithRights";
Пароль = "SuperSecretPass123";
ИмяФайла = "export_data.txt";
// Попытка создать COM-объект для работы с сетью
Попытка
WshNetwork = Новый COMОбъект("WScript.Network");
Исключение
Сообщить("Не удалось создать WScript.Network: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Подключаем сетевой ресурс.
// Параметры MapNetworkDrive:
// 1. Буква диска (пустая строка "", если не хотим занимать букву, но это работает не всегда стабильно, лучше использовать NET USE)
// Однако MapNetworkDrive чаще используют для создания буквы диска.
// Альтернативный и более надежный вариант для простого доступа без буквы - использование команды NET USE через WScript.Shell (см. ниже)
// Но если мы хотим использовать именно WScript.Network для маппинга:
БукваДиска = "Z:";
// Сначала попробуем отключить диск, если он вдруг завис от прошлой сессии
Попытка
WshNetwork.RemoveNetworkDrive(БукваДиска, Истина, Истина);
Исключение
// Игнорируем ошибку, если диска не было
КонецПопытки;
Попытка
// Подключаем диск с авторизацией
WshNetwork.MapNetworkDrive(БукваДиска, ПутьКРесурсу, Ложь, Логин, Пароль);
// Теперь мы можем писать в Z:\
ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.УстановитьТекст("Важные данные для выгрузки");
ПолныйПуть = БукваДиска + "\" + ИмяФайла;
ТекстДок.Записать(ПолныйПуть, КодировкаТекста.UTF8);
Сообщить("Файл успешно записан: " + ПолныйПуть);
// Отключаем диск после работы
WshNetwork.RemoveNetworkDrive(БукваДиска, Истина, Истина);
Исключение
Сообщить("Ошибка при работе с сетевым ресурсом: " + ОписаниеОшибки());
// Пытаемся почистить за собой в случае ошибки
Попытка WshNetwork.RemoveNetworkDrive(БукваДиска, Истина, Истина); Исключение КонецПопытки;
КонецПопытки;
КонецПроцедуры
Иногда метод MapNetworkDrive может быть неудобен, так как требует свободной буквы диска, а в многопоточной серверной среде это может вызвать коллизии (если разные фоновые задания попытаются занять букву Z:). Более гибкий способ — использовать команду CMD net use для авторизации на ресурсе без назначения буквы диска.
Разберем, как это сделать с помощью объекта WScript.Shell.
&НаСервере
Процедура ЗаписатьЧерезNetUse()
ПутьКШаре = "\\192.168.1.50\Exchange";
Логин = "MyDomain\UserWrite";
Пароль = "Pa$$word";
Шелл = Новый COMОбъект("WScript.Shell");
// Формируем команду подключения.
// /delete нужен, чтобы разорвать старые подключения к этому ресурсу, если они "зависли"
// >nul 2>&1 скрывает вывод команды, чтобы не засорять логи, но для отладки можно убрать
КомандаУдаления = "net use """ + ПутьКШаре + """ /delete /y";
Шелл.Run(КомандаУдаления, 0, Истина);
// Команда подключения
КомандаПодключения = "net use """ + ПутьКШаре + """ """ + Пароль + """ /user:""" + Логин + """";
КодВозврата = Шелл.Run(КомандаПодключения, 0, Истина);
Если КодВозврата <> 0 Тогда
Сообщить("Ошибка подключения к сетевой папке. Код ошибки: " + КодВозврата);
Возврат;
КонецЕсли;
// Теперь, когда соединение установлено, процесс сервера 1С имеет доступ к пути
Попытка
Текст = Новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("Проверка записи");
Текст.Записать(ПутьКШаре + "\test_file.txt");
Сообщить("Запись прошла успешно!");
Исключение
Сообщить("Ошибка записи: " + ОписаниеОшибки());
КонецПопытки;
// Отключаемся (хороший тон)
Шелл.Run(КомандаУдаления, 0, Истина);
КонецПроцедуры
Важные нюансы метода:
0 в методе Run означает скрытый запуск окна консоли (чтобы на сервере не мелькали черные окна).Истина в методе Run заставляет 1С ждать завершения команды, прежде чем идти дальше. Это критично, так как нам нужно убедиться, что диск подключился, прежде чем писать файл.Если служба безопасности запрещает хранить пароли в коде 1С, нам придется изменить подход к передаче файлов. Рассмотрим варианты, когда мы не пробиваемся через права, а обходим их.
Если целевой сервер поддерживает FTP, это идеальный вариант. Права на доступ к файловой системе сервера регулируются самой службой FTP, и пользователь 1С (как процесс ОС) здесь не участвует. Мы используем встроенный объект FTPСоединение.
// Пример записи через FTP
Соединение = Новый FTPСоединение("server_address", 21, "ftp_user", "ftp_pass");
Соединение.УстановитьТекущийКаталог("/upload/");
Соединение.Записать(ЛокальныйПутьКВременномуФайлу, "remote_file.txt");
Это классический паттерн интеграции в закрытых контурах безопасности. Вместо того чтобы мучиться с сетевыми путями, можно использовать O-Bridges для простого обмена данными между базами 1С и сторонними приложениями. Если же вы настраиваете процесс вручную, схема выглядит так:
USR1CV8 есть полные права (например, C:\Exchange1C\).C:\Exchange1C\ и перемещает их в целевую защищенную сетевую папку.Этот метод полностью снимает головную боль с правами доступа с программиста 1С и перекладывает ответственность за доставку файла на администраторов.
Давайте резюмируем. Если вы столкнулись с ошибкой доступа при записи файла на сервере:
ПривилегированныйРежим — он не поможет с правами файловой системы.WScript.Network или net use для временной авторизации.