При разработке механизмов обмена файлами или автоматизации архивации в 1С программисты часто сталкиваются с ситуацией, когда стандартная функция КопироватьФайл отказывается работать. Проблема осложняется, когда копирование происходит между сетевыми ресурсами (UNC-пути), а сами файлы имеют атрибут "Только чтение". В данной статье мы подробно разберем, почему возникают ошибки доступа, как правильно реализовать рекурсивный обход папок и какие альтернативные системные инструменты можно задействовать для надежной работы кода на стороне сервера.
Прежде чем приступать к написанию кода, проанализируем ситуацию, когда ручное копирование работает, а программное из 1С — нет. Чаще всего причина кроется в учетной записи, под которой запущена служба "Агент сервера 1С:Предприятие". Рассмотрим основные сценарии:
USR1CV8. Эта запись не имеет прав доступа к сетевым ресурсам (папкам вида \\server\share). Чтобы сервер 1С мог "видеть" внешние каталоги, служба должна быть запущена от имени доменного пользователя, которому предоставлены права NTFS и права общего доступа на обе папки (источник и приемник).КопироватьФайл завершится с ошибкой "Доступ запрещен", даже если у пользователя есть полные права. Система не может перезаписать файл, защищенный от изменений.Рассмотрим, как модифицировать код, чтобы он корректно обрабатывал вложенные папки и снимал блокирующие атрибуты. Ключевым моментом здесь является проверка существования файла в приемнике и явное снятие атрибута ТолькоЧтение перед попыткой копирования.
Проанализируем пример функции, которая подготавливает структуру каталогов:
&НаСервере
Функция СкопироватьФайлБезопасно(ИмяФайлаИсточник, ИмяФайлаНазначение)
ФайлПриемник = Новый Файл(ИмяФайлаНазначение);
// Проверим наличие целевого каталога и создадим его при необходимости
КаталогНазначения = ФайлПриемник.Путь;
СоздатьКаталог(КаталогНазначения);
// Важный этап: снятие атрибута "Только чтение" с существующего файла
Если ФайлПриемник.Существует Тогда
Если ФайлПриемник.ПолучитьТолькоЧтение() Тогда
ФайлПриемник.УстановитьТолькоЧтение(Ложь);
КонецЕсли;
КонецЕсли;
// Снятие атрибута с источника (иногда требуется для некоторых сетевых ФС)
ФайлИсточник = Новый Файл(ИмяФайлаИсточник);
Если ФайлИсточник.Существует И ФайлИсточник.ПолучитьТолькоЧтение() Тогда
ФайлИсточник.УстановитьТолькоЧтение(Ложь);
КонецЕсли;
Попытка
КопироватьФайл(ИмяФайлаИсточник, ИмяФайлаНазначение);
Возврат Истина;
Исключение
ЗаписьЖурналаРегистрации("КопированиеФайлов", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка: " + ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
КонецФункции
Обратите внимание: использование Файл.Путь позволяет автоматически получить родительскую папку без сложных манипуляций со строками и поиском символа \. Мы рекомендуем всегда проверять результат работы функции СоздатьКаталог, так как она создает всю иерархию папок сразу.
Если стандартные функции 1С продолжают выдавать неочевидные ошибки, мы можем обратиться к мощностям операционной системы через COMОбъект. Объект Scripting.FileSystemObject предоставляет более гибкие инструменты для работы с файловой системой Windows, включая принудительную перезапись файлов с атрибутом Read-Only.
Посмотрим на пример реализации:
&НаСервере
Процедура КопироватьЧерезFSO(ПутьИсточник, ПутьПриемник)
Попытка
FSO = Новый COMОбъект("Scripting.FileSystemObject");
// Метод CopyFile(Source, Destination, Overwrite)
// Третий параметр True позволяет перезаписывать файлы даже с атрибутом ReadOnly в некоторых случаях
FSO.CopyFile(ПутьИсточник, ПутьПриемник, Истина);
Исключение
Сообщить("Ошибка FSO: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Преимущество FSO заключается в том, что он работает на уровне системных вызовов Windows и иногда лучше справляется с очень длинными путями (более 255 символов) и специфическими сетевыми блокировками.
Для задачи, описанной автором темы (когда нужно перенести структуру папок из Excel), необходимо реализовать рекурсию. Проанализируем правильный алгоритм обхода:
&НаСервере
Процедура СкопироватьРекурсивно(Откуда, Куда)
// Получаем список всех файлов и папок в источнике
МассивФайлов = НайтиФайлы(Откуда, "*.*");
Для Каждого ОбъектФайл Из МассивФайлов Цикл
НовыйПутьНазначения = Куда + "\" + ОбъектФайл.Имя;
Если ОбъектФайл.ЭтоКаталог Тогда
// Создаем папку в приемнике и идем глубже
СоздатьКаталог(НовыйПутьНазначения);
СкопироватьРекурсивно(ОбъектФайл.ПолноеИмя, НовыйПутьНазначения);
Иначе
// Вызываем нашу безопасную функцию копирования файла
СкопироватьФайлБезопасно(ОбъектФайл.ПолноеИмя, НовыйПутьНазначения);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В этом коде мы разделяем логику обработки каталогов и файлов. Важно помнить, что при работе с сетевыми путями использование функции НРег() (приведение к нижнему регистру) может быть излишним и иногда вредным, если сервер хранения данных чувствителен к регистру (например, при обращении к Linux-шарам через Samba).
Выясним еще несколько важных моментов, которые помогут избежать проблем при внедрении:
Z:\Folder) в серверном коде. Для службы Z: не существует. Всегда указывайте полный сетевой путь: \\ServerName\ShareName\Path.ЗаписьЖурналаРегистрации, чтобы системный администратор мог отследить, на каком именно файле "споткнулся" процесс.Robocopy через ЗапуститьПриложение. Она умеет работать в многопоточном режиме и автоматически возобновлять копирование при сбоях сети.Таким образом, для успешного копирования файлов во вложенных папках нам необходимо обеспечить три условия: правильную учетную запись службы 1С для доступа к сети, программное снятие атрибута "Только чтение" и корректный рекурсивный обход с созданием структуры каталогов. Сочетание этих методов гарантирует стабильную работу обработки в любых условиях.