Проверка наличия файла на диске во встроенном языке 1С 8.3
Содержание
Проверить, существует ли файл на диске, в 1С 8.3 проще всего так: создать объект Новый Файл(ПолныйПуть) и вызвать у него метод Существует(). Он вернёт Истина или Ложь и не выбросит исключение, даже если такого пути нет.
Частое заблуждение: «раз объект Файл создался, значит файл есть». Нет. Конструктор Новый Файл ничего не проверяет, он просто запоминает строку пути. Файл может отсутствовать, путь может указывать на папку, а код может выполняться вообще на другом компьютере. Разберём каждый из этих случаев с кодом.
Базовая проверка существования файла через объект Файл
Минимальный рабочий вариант для серверного кода или толстого клиента:
ПутьКФайлу = "C:\Обмен\выгрузка.xml";
Файл = Новый Файл(ПутьКФайлу);
Если Файл.Существует() Тогда
Сообщить("Файл найден: " + Файл.ПолноеИмя);
Иначе
Сообщить("Файла нет: " + ПутьКФайлу);
КонецЕсли;
У объекта Файл, кроме Существует(), есть полезные свойства, которые не требуют обращения к диску. Они просто разбирают строку пути:
| Свойство | Что вернёт для C:\Обмен\выгрузка.xml |
|---|---|
ПолноеИмя | C:\Обмен\выгрузка.xml |
Имя | выгрузка.xml |
ИмяБезРасширения | выгрузка |
Расширение | .xml (с точкой) |
Путь | C:\Обмен\ |
💡 Свойство Расширение возвращается с точкой. При сравнении пишите Файл.Расширение = ".xml", а не "xml". И приводите регистр через НРег(): файл может называться ВЫГРУЗКА.XML.
Файл или каталог: что именно нашлось
Существует() возвращает Истина и для файла, и для папки. Если вы ждёте именно файл, а пользователь выбрал каталог, проверка пройдёт, а чтение упадёт. Поэтому уточняйте:
Файл = Новый Файл(ПутьКФайлу);
Если Не Файл.Существует() Тогда
ВызватьИсключение "Не найден путь: " + ПутьКФайлу;
ИначеЕсли Файл.ЭтоКаталог() Тогда
ВызватьИсключение "Указана папка, а нужен файл: " + ПутьКФайлу;
КонецЕсли;
// здесь точно есть файл
Аналогично проверяется наличие каталога: Существует() плюс ЭтоКаталог(). Методы ЭтоФайл() и ЭтоКаталог() обращаются к диску, поэтому вызывайте их после Существует().
Пустая строка и пробелы
Путь часто приходит из реквизита формы или константы. Пустую строку и случайные пробелы лучше отсечь до создания объекта:
ПутьКФайлу = СокрЛП(Объект.ПутьКФайлу);
Если ПустаяСтрока(ПутьКФайлу) Тогда
ВызватьИсключение "Не указан путь к файлу";
КонецЕсли;
Это дешевле и понятнее пользователю, чем разбирать последствия проверки пустого пути.
Как обрабатывать исключения при работе с путями
Сам Существует() на отсутствующий файл не ругается. Исключения появляются на следующих шагах: при чтении, копировании, удалении, при запросе размера или времени изменения. А ещё в безопасном режиме, где доступ к файловой системе запрещён целиком.
Типовая обёртка выглядит так:
Попытка
Файл = Новый Файл(ПутьКФайлу);
Если Не Файл.Существует() Тогда
Возврат Неопределено;
КонецЕсли;
Размер = Файл.Размер();
Изменен = Файл.ПолучитьВремяИзменения();
Исключение
ТекстОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("Обмен.ЧтениеФайла",
УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
ВызватьИсключение "Не удалось прочитать файл " + ПутьКФайлу
+ ": " + ТекстОшибки;
КонецПопытки;
Что здесь важно:
- 🧱 Текст ошибки пишите в журнал регистрации. Пользователь закроет окно, а администратору нужна причина.
- 🔁 Не глушите исключение пустым блоком
Исключение ... КонецПопытки. Код молча пойдёт дальше с пустыми данными. - 🕒 Между проверкой и чтением файл может исчезнуть: его забрал другой процесс обмена или удалил антивирус. Проверка
Существует()не гарантирует успешного чтения, поэтому чтение тоже держите вПопытка.
Разделители пути и регистр букв
Склеивать путь вручную через "\" — главный источник ошибок при переносе кода на Linux. Платформа даёт функции для разделителя:
ПолучитьРазделительПути()— разделитель на клиенте;ПолучитьРазделительПутиСервера()— разделитель на сервере.
Разделитель = ПолучитьРазделительПутиСервера();
ПолныйПуть = КаталогОбмена + Разделитель + ИмяФайла;
На сервере под Linux имена файлов чувствительны к регистру. Выгрузка.xml и выгрузка.xml там — два разных файла. Код, который годами работал на Windows, после переезда сервера начинает «не находить» файлы.
Проверка файла на клиенте и на сервере 1С 8.3
Главный вопрос любой проверки: на чьём диске ищем. Код с директивой &НаСервере смотрит на файловую систему сервера 1С:Предприятия. Код &НаКлиенте — на компьютер пользователя.
| Где выполняется код | Чей диск проверяется | От чьего имени |
|---|---|---|
&НаСервере, клиент-серверная база | Компьютер с сервером 1С | Учётная запись службы сервера 1С |
&НаСервере, файловая база | Компьютер пользователя | Пользователь Windows |
&НаКлиенте, тонкий клиент | Компьютер пользователя | Пользователь Windows |
&НаКлиенте, веб-клиент | Компьютер пользователя | Только через расширение работы с файлами |
Отсюда типовая жалоба: «на моём компьютере файл есть, а 1С пишет, что нет». Файл лежит на C:\ пользователя, а проверка идёт на сервере.
Сетевые диски вида Z:\ служба сервера 1С не видит. Буквы дисков подключаются в сеансе пользователя, а у службы своего сеанса с ними нет. Для серверного кода используйте UNC-путь: \\fileserver\obmen\выгрузка.xml. И дайте учётной записи службы права на эту папку.
Асинхронная проверка на клиенте
В конфигурациях на управляемых формах с отключённым режимом модальности и синхронных вызовов метод Существует() на клиенте вызывать нельзя. Платформа предлагает два варианта.
Через описание оповещения:
&НаКлиенте
Процедура ПроверитьФайл(Команда)
Файл = Новый Файл(Объект.ПутьКФайлу);
Оповещение = Новый ОписаниеОповещения("ПослеПроверкиФайла", ЭтотОбъект);
Файл.НачатьПроверкуСуществования(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПроверкиФайла(Существует, ДополнительныеПараметры) Экспорт
Если Не Существует Тогда
Сообщить("Файл не найден");
КонецЕсли;
КонецПроцедуры
На платформе 8.3.18 и новее код короче, через асинхронный метод и Ждать:
&НаКлиенте
Асинх Процедура ПроверитьФайл(Команда)
Файл = Новый Файл(Объект.ПутьКФайлу);
Существует = Ждать Файл.СуществуетАсинх();
Если Не Существует Тогда
Сообщить("Файл не найден");
КонецЕсли;
КонецПроцедуры
💡 В веб-клиенте прежде любых операций с локальными файлами подключите расширение работы с файлами. Без него браузер не даст 1С заглянуть на диск пользователя, и проверка просто не выполнится.
Если файл нужен и на клиенте, и на сервере
Частая задача: пользователь выбирает файл у себя, а обрабатывает его сервер. Проверять путь на сервере в этом случае бессмысленно. Правильный порядок: проверить на клиенте, передать содержимое во временное хранилище, а на сервере работать уже с двоичными данными или со своим временным файлом.
Временные файлы и каталоги: создание и проверка
Для промежуточных данных не придумывайте пути вроде C:\Temp\1.txt. На сервере такой папки может не быть, а у службы может не оказаться прав на запись.
Используйте функции платформы:
КаталогВременныхФайлов()— путь к каталогу временных файлов текущего пользователя ОС;ПолучитьИмяВременногоФайла("xml")— уникальное имя с нужным расширением.
ИмяВременного = ПолучитьИмяВременногоФайла("xml");
// файла по этому пути ещё нет: функция только придумала имя
ДвоичныеДанные.Записать(ИмяВременного);
Попытка
ОбработатьФайл(ИмяВременного);
Исключение
УдалитьФайлы(ИмяВременного);
ВызватьИсключение;
КонецПопытки;
УдалитьФайлы(ИмяВременного);
ПолучитьИмяВременногоФайла() не создаёт файл. Проверка Существует() сразу после неё вернёт Ложь, и это нормально.
Если нужен временный каталог для нескольких файлов, получите уникальное имя и создайте папку через СоздатьКаталог(). После работы удалите её целиком: УдалитьФайлы(ИмяКаталога).
В типовых конфигурациях на БСП для этого есть общий модуль ФайловаяСистема с функциями для временных файлов и каталогов. Если он есть в вашей конфигурации, удобнее пользоваться им: он учитывает особенности клиент-серверного режима.
Убирайте за собой
Временные файлы, которые никто не удалил, копятся годами. На сервере это гигабайты в каталоге временных файлов службы. Удаляйте файл в той же процедуре, которая его создала, в том числе в ветке Исключение.
Права доступа и атрибуты файла
Факт существования ещё не значит, что файл можно прочитать или перезаписать. Отдельного метода «есть ли у меня права» у объекта Файл нет. Права проверяются действием.
Атрибуты, которые можно прочитать
| Метод | Что делает |
|---|---|
ПолучитьТолькоЧтение() | Истина, если стоит атрибут «Только чтение» |
УстановитьТолькоЧтение(Ложь) | Снимает атрибут, если хватает прав |
ПолучитьНевидимость() | Истина для скрытого файла |
ПолучитьВремяИзменения() | Дата последнего изменения |
Размер() | Размер в байтах |
Все эти методы обращаются к диску. Для отсутствующего файла они выбрасывают исключение, поэтому вызывайте их после Существует() и внутри Попытка.
Проверка права на чтение и запись
Надёжный способ — попробовать. Для чтения откройте файл и сразу закройте:
Функция ФайлДоступенДляЧтения(ПутьКФайлу)
Попытка
Чтение = Новый ЧтениеТекста(ПутьКФайлу);
Чтение.Закрыть();
Возврат Истина;
Исключение
Возврат Ложь;
КонецПопытки;
КонецФункции
Для права записи в каталог создайте пробный файл с уникальным именем и удалите его:
Функция КаталогДоступенДляЗаписи(Каталог)
ПробныйФайл = Каталог + ПолучитьРазделительПутиСервера()
+ Строка(Новый УникальныйИдентификатор) + ".tmp";
Попытка
Запись = Новый ЗаписьТекста(ПробныйФайл);
Запись.Закрыть();
УдалитьФайлы(ПробныйФайл);
Возврат Истина;
Исключение
Возврат Ложь;
КонецПопытки;
КонецФункции
Не проверяйте права записью в рабочий файл. Открытие существующего файла на запись через ЗаписьТекста перезапишет его содержимое.
Ещё одна причина отказа в доступе — профили безопасности кластера и безопасный режим. Внешние обработки и расширения, запущенные в безопасном режиме, к файловой системе не допускаются. Ошибка в этом случае говорит о нарушении прав, хотя с правами Windows всё в порядке.
Файл есть, но его ещё дописывают
В обменах с внешними программами файл появляется на диске раньше, чем заканчивается запись. Проверка Существует() уже вернула Истина, а внутри половина XML. Разбор падает с ошибкой формата.
Надёжные приёмы:
- 📝 Договоритесь с отправителем о временном имени. Программа пишет
выгрузка.xml.part, а по окончании переименовывает ввыгрузка.xml. 1С ищет только готовое имя. - 🚩 Используйте файл-флаг. Рядом с данными отправитель кладёт пустой
выгрузка.ready. Сначала проверяете флаг, потом читаете данные. - ⏱ Сравнивайте размер и время изменения при двух запусках. Если за несколько минут они не изменились, файл дописан.
Не крутите цикл ожидания внутри одной процедуры. Встроенного «сна» на сервере нет, и пустой цикл просто нагрузит процессор. Удобнее регламентное задание, которое запускается раз в 5–10 минут и забирает только готовые файлы.
Альтернативные способы и устаревшие подходы
Объект Файл — основной инструмент, но не единственный.
| Способ | Когда уместен | Минусы |
|---|---|---|
Новый Файл(...).Существует() | Проверка одного известного пути | Синхронный, на клиенте запрещён при отключённых синхронных вызовах |
НачатьПроверкуСуществования() / СуществуетАсинх() | Клиентский код управляемых форм | Код длиннее, нужен обработчик или Асинх |
НайтиФайлы(Каталог, Маска) | Имя известно не полностью: выгрузка_*.xml | Перебирает каталог, на больших папках медленно |
| Попытка открыть файл | Нужно сразу и проверить, и прочитать | Исключение как штатная ветка хуже читается |
Поиск по маске выглядит так:
Найденные = НайтиФайлы(КаталогОбмена, "выгрузка_*.xml", Ложь);
Если Найденные.Количество() = 0 Тогда
Сообщить("Новых файлов выгрузки нет");
КонецЕсли;
Третий параметр — искать ли во вложенных папках. Для клиентского кода есть асинхронные пары: НачатьПоискФайлов() и, на новых платформах, НайтиФайлыАсинх().
Что пора убрать из старого кода
- 🗑
НайтиФайлы(ПолныйПутьКФайлу)для проверки одного файла. Это рудимент 8.0–8.1, когда объект Файл был беднее. СейчасСуществует()короче и понятнее. - 🗑 Вызов
cmd /c if exist ...черезЗапуститьПриложениеили COM-объектScripting.FileSystemObject. Работает только на Windows, ломается на Linux-сервере и в веб-клиенте. - 🗑 Жёсткие пути
C:\Temp\иD:\Обмен\прямо в коде. Выносите их в константу или настройку, проверяйте при старте обмена.
Частые ошибки при проверке наличия файла
Частые вопросы
Выбрасывает ли Новый Файл() исключение, если файла нет?
Как проверить, что каталог существует, а не файл?
Существует(), затем ЭтоКаталог(). Оба должны вернуть Истина.Можно ли проверить наличие файла по URL или на FTP через объект Файл?
HTTPСоединение и FTPСоединение.Чем отличается КаталогВременныхФайлов() на клиенте и на сервере?
Как проверить, что файл уже помещён во временное хранилище?
ЭтоАдресВременногоХранилища() и получите данные через ПолучитьИзВременногоХранилища().