При программной работе с файлами на сервере 1С (особенно при обработке изображений, генерации PDF-документов или вызове внешних консольных утилит вроде ImageMagick) разработчики часто сталкиваются с неприятной ошибкой: «Ошибка при вызове метода контекста (УдалитьФайлы)... Ошибка доступа к файлу. 5(0x00000005): Access is denied». При этом вручную под своей учетной записью администратор может легко удалить этот файл.
В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем неочевидный парадокс метода ПереместитьФайл, выясним истинные причины блокировок операционной системы и рассмотрим эффективные программные и системные способы решения этой проблемы.
Если ваша база данных работает в клиент-серверном режиме (на базе MS SQL Server, PostgreSQL и т.д.), любой код, написанный без явной директивы &НаКлиенте, выполняется на стороне сервера приложений 1С. Это означает следующее:
C:\Файлы1с\20191224\GARD05_1.jpg) обрабатывается локально на сервере, а не на вашем рабочем компьютере.USR1CV8 или аналогичный системный аккаунт).Даже если вы лично зашли в Windows под учетной записью Администратора и можете вручную удалять любые файлы в папке C:\Файлы1с\, у пользователя USR1CV8 может попросту не быть прав на удаление в этой директории. Ему необходимы полные права (Full Control) на уровне безопасности NTFS.
В процессе обсуждения проблемы часто замечают удивительный факт: метод УдалитьФайлы вызывает ошибку доступа, но метод ПереместитьФайл в другую директорию отрабатывает успешно, и файл благополучно исчезает из исходной папки. Почему так происходит?
Это связано с логикой работы файловой системы NTFS под управлением ОС Windows:
Метод платформы 1С УдалитьФайлы() имеет жесткое ограничение: он всегда завершается ошибкой доступа (Error 5), если у удаляемого файла установлен системный атрибут «Только чтение». Внешние конвертеры или процессы копирования файлов часто автоматически присваивают этот атрибут результирующим файлам.
Рассмотрим, как гарантированно снять этот атрибут перед попыткой удаления файла:
ФайлНаДиске = Новый Файл(СтароеИмя);
Если ФайлНаДиске.Существует() Тогда
// Снимаем защиту от записи и удаления
ФайлНаДиске.УстановитьТолькоЧтение(Ложь);
Попытка
УдалитьФайлы(СтароеИмя);
Исключение
ЗаписьЖурналаРегистрации("УдалениеФайлов", УровеньЖурналаРегистрации.Предупреждение, , ,
"Не удалось удалить файл: " + СтароеИмя + ". Описание: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЕсли;
Часто разработчики забывают, что файл может быть заблокирован самой системой 1С в соседних строках кода. Если до вызова удаления или работы внешнего конвертера в коде выполнялось чтение этого изображения (например, для проверки размеров, получения двоичных данных или отображения на форме), платформа могла захватить дескриптор файла.
Объекты типа Картинка и ДвоичныеДанные, созданные через конструкторы путей (например, Новый Картинка(Путь)), блокируют файл на диске. Файл освобождается только тогда, когда объект удаляется из оперативной памяти сборщиком мусора 1С, работа которого непредсказуема по времени.
Чтобы избежать этой проблемы, перед вызовом удаления необходимо явно очищать переменные, ссылающиеся на файл, присваивая им значение Неопределено:
МояКартинка = Неопределено;
МоиДвоичныеДанные = Неопределено;
// Только после этого вызываем удаление файла
УдалитьФайлы(СтароеИмя);
Даже если в коде запуска внешнего приложения (например, утилиты ImageMagick) установлен параметр ожидания завершения (значение Истина в методе ЗапуститьПриложение), сразу после закрытия утилиты файл может стать временно недоступным:
Если 1С пытается выполнить метод УдалитьФайлы() ровно в ту же миллисекунду, когда антивирус проверяет файл, операционная система вернет ошибку доступа (Error 5 или Error 32).
Решением является использование цикла попыток удаления с микропаузой. Проанализируем пример кода, реализующий этот надежный алгоритм:
УспешноУдалено = Ложь;
Для Итератор = 1 По 5 Цикл // Выполняем до 5 попыток удаления
Попытка
УдалитьФайлы(СтароеИмя);
УспешноУдалено = Истина;
Прервать; // Удаление прошло успешно, выходим из цикла
Исключение
// Файл временно заблокирован внешним процессом.
// Выдерживаем паузу в 1 секунду перед следующей попыткой.
// Для серверного кода можно использовать метод ожидания:
ПриостановитьВыполнение(1);
КонецПопытки;
КонецЦикла;
Если Не УспешноУдалено Тогда
ВызватьИсключение "Не удалось удалить файл после нескольких попыток: " + СтароеИмя;
КонецЕсли;
Объединим все лучшие практики в одну надежную экспортную процедуру, которую вы сможете использовать в своих обработках для гарантированного удаления файлов без возникновения ошибок доступа:
Процедура БезопасноУдалитьФайл(Знач ПолныйПутьКФайлу) Экспорт
ФайлНаДиске = Новый Файл(ПолныйПутьКФайлу);
Если Не ФайлНаДиске.Существует() Тогда
Возврат;
КонецЕсли;
// Шаг 1: Принудительно снимаем атрибут "Только чтение"
Если ФайлНаДиске.ПолучитьТолькоЧтение() Тогда
Попытка
ФайлНаДиске.УстановитьТолькоЧтение(Ложь);
Исключение
// Запишем предупреждение, если не удалось изменить атрибуты
ЗаписьЖурналаРегистрации("УдалениеФайлов.Атрибуты",
УровеньЖурналаРегистрации.Предупреждение, , ,
"Не удалось снять атрибут Только Чтение с файла: " + ПолныйПутьКФайлу);
КонецПопытки;
КонецЕсли;
// Шаг 2: Удаление файла с защитой от блокировок ОС (цикл с паузой)
УспешноУдалено = Ложь;
МаксимальноеКоличествоПопыток = 5;
Для ПопыткаУдаления = 1 По МаксимальноеКоличествоПопыток Цикл
Попытка
УдалитьФайлы(ПолныйПутьКФайлу);
УспешноУдалено = Истина;
Прервать;
Исключение
Если ПопыткаУдаления < МаксимальноеКоличествоПопыток Тогда
// Ждем освобождения дескриптора операционной системой
ПриостановитьВыполнение(1);
КонецЕсли;
КонецПопытки;
КонецЦикла;
// Шаг 3: Если файл так и не удалился, применяем резервный метод "Перемещение во временный каталог"
Если Не УспешноУдалено Тогда
Попытка
КаталогВременныхФайлов = КаталогВременныхФайлов();
ИмяВременногоФайла = КаталогВременныхФайлов + ПолучитьИмяВременногоФайла(ФайлНаДиске.Расширение);
ПереместитьФайл(ПолныйПутьКФайлу, ИмяВременногоФайла);
// Пробуем удалить уже перемещенный временный файл
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Если и это не помогло, фиксируем критическую ошибку прав NTFS на сервере
ВызватьИсключение "Критическая ошибка доступа при удалении файла: " + ПолныйПутьКФайлу
+ Символы.ПС + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Применяя данный комплексный подход, мы полностью исключаем влияние временных блокировок операционной системы, обходим ограничения атрибутов файлов и нивелируем архитектурные особенности прав доступа серверных служб 1С.