Работа с файловой системой в контексте HTTP-сервисов часто преподносит сюрпризы даже опытным разработчикам. Ситуация, когда код идеально работает в обычной внешней обработке, но «тихо» ломается при вызове через веб-сервер, — классическая проблема. В этой статье мы подробно разберем, почему так происходит, и как правильно реализовать создание сетевых каталогов и запись файлов на сервере.
Первое, что мы должны проанализировать, — это от чьего имени выполняется код. Когда вы запускаете внешнюю обработку, 1С работает от имени вашей учетной записи Windows. У вас есть права на сетевую папку, ваше окружение инициализировано, сетевые диски подключены.
Однако, когда тот же самый код вызывается через HTTP-сервис, ситуация кардинально меняется:
ragent (обычно это USR1CV8). У этой учетной записи по умолчанию нет прав на сетевые ресурсы.Проанализируем особенность платформы 1С:Предприятие (особенно версий ниже 8.3.11). В этих версиях метод СоздатьКаталог() часто не выбрасывает исключение, если папка не была создана из-за нехватки прав. Программа просто идет дальше, а следующая строка кода, пытающаяся записать файл в несуществующий каталог, вызывает ошибку.
Рассмотрим пример кода, который часто приводит к ошибке:
ИмяПапки = "\\Server\Share\NewFolder";
СоздатьКаталог(ИмяПапки);
// На платформе 8.3.6 здесь не будет ошибки, даже если папка не создалась
Файл = Новый Файл(ИмяПапки);
Если Не Файл.Существует() Тогда
// Вот здесь мы внезапно обнаруживаем, что каталога нет
КонецЕсли;
Одной из самых коварных ошибок является попытка использовать ПолучитьИзВременногоХранилища() внутри метода HTTP-сервиса. Рассмотрим ситуацию: автор темы передает адрес временного хранилища и пытается записать данные на диск.
Важно понимать: HTTP-запрос в 1С — это новый сеанс. Временное хранилище живет ровно столько, сколько живет сеанс. Если вы поместили файл в хранилище в одном сеансе (например, в обработке), а пытаетесь достать его в методе POST или GET HTTP-сервиса, вы получите Неопределено. Именно поэтому возникает ошибка «Значение не является значением объектного типа (Записать)».
Чтобы этого избежать, мы должны передавать двоичные данные файла непосредственно в теле HTTP-запроса. Посмотрим, как это реализовать правильно:
// В методе HTTP-сервиса (например, POST)
ДвоичныеДанные = Запрос.ПолучитьТелоКакДвоичныеДанные();
Если ДвоичныеДанные = Неопределено Тогда
// Обработка ошибки: тело запроса пустое
КонецЕсли;
При работе с сетевыми каталогами (пути вида \\Server\Folder) возникает эффект задержки. После команды СоздатьКаталог() операционной системе требуется время (иногда до нескольких сотен миллисекунд), чтобы обновить информацию в сетевом кэше. Если мы проверим Файл.Существует() мгновенно после создания, система может вернуть Ложь.
Рекомендуем использовать небольшую паузу или цикл проверки:
СоздатьКаталог(СетевойПуть);
Для Итерация = 1 По 5 Цикл
ТестовыйФайл = Новый Файл(СетевойПуть);
Если ТестовыйФайл.Существует() Тогда
Прервать;
КонецЕсли;
// Небольшая задержка (можно использовать Sleep через COM или просто пустой цикл)
КонецЦикла;
В обсуждении предлагалось использовать xcopy через КомандаСистемы(). Мы крайне не рекомендуем этот способ для HTTP-сервисов по следующим причинам:
cmd.exe.КомандаСистемы часто просто игнорируется или вызывает ошибку инициализации модуля.Вместо этого мы должны использовать встроенные средства 1С, такие как КопироватьФайл() или объекты для работы с двоичными данными.
Рассмотрим пошагово, как правильно построить процесс приема файла через HTTP-сервис и сохранение его в создаваемый каталог:
Запрос.ПолучитьТелоКакДвоичныеДанные(). Забудьте про временное хранилище (адреса e1cib/tempstorage/...) в контексте вызова внешних систем.КаталогВременныхФайлов(). У службы 1С гарантированно есть туда доступ.КопироватьФайл() для перемещения файла из временной папки в целевой сетевой каталог.Посмотрим на пример надежного кода:
Функция СохранитьФайлИзЗапроса(Запрос)
// 1. Получаем данные
ДанныеФайла = Запрос.ПолучитьТелоКакДвоичныеДанные();
ИмяФайла = Запрос.Заголовки.Получить("X-File-Name"); // Передаем имя в заголовке
// 2. Определяем пути
СетевойКаталог = "\\FileServer\Uploads\" + Формат(ТекущаяДата(), "ДФ=yyyyMMdd");
// 3. Попытка создания каталога с проверкой
Попытка
СоздатьКаталог(СетевойКаталог);
Исключение
ЗаписьЖурналаРегистрации("ОшибкаФайлов", УровеньЖурналаРегистрации.Ошибка, , ,
"Не удалось создать каталог: " + ОписаниеОшибки());
Возврат Новый HTTPСервисОтвет(500);
КонецПопытки;
// 4. Запись файла
ПолныйПуть = СетевойКаталог + "\" + ИмяФайла;
Попытка
ДанныеФайла.Записать(ПолныйПуть);
Исключение
// Здесь мы поймаем ошибку прав доступа ОС
ЗаписьЖурналаРегистрации("ОшибкаФайлов", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка записи в сетевой каталог: " + ОписаниеОшибки());
Возврат Новый HTTPСервисОтвет(403); // Forbidden
КонецПопытки;
Возврат Новый HTTPСервисОтвет(200);
КонецФункции
Если вы программист и у вас нет админского доступа к серверу, проанализируем ситуацию с помощью простого теста. Напишите временную внешнюю обработку, которая делает СоздатьКаталог(), и запустите её на сервере через консоль заданий (регламентное задание). Если регламентное задание упадет с ошибкой доступа — значит, проблема в правах пользователя USR1CV8. Если регламентное задание сработает, а HTTP-сервис нет — проблема в настройках веб-сервера (IIS/Apache).
Важный совет: Всегда проверяйте существование сетевого пути перед началом работы. Помните, что для сервера 1С путь D:\Files и для вас тот же путь — это физически одно и то же место, а сетевой диск Z:\ у сервера может просто не существовать, так как диски монтируются для конкретного пользователя при входе в систему.