Проверка наличия файла на диске во встроенном языке 1С 8.3

· 9 мин

Содержание

Проверить, существует ли файл на диске, в 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. Разбор падает с ошибкой формата.

Надёжные приёмы:

  1. 📝 Договоритесь с отправителем о временном имени. Программа пишет выгрузка.xml.part, а по окончании переименовывает в выгрузка.xml. 1С ищет только готовое имя.
  2. 🚩 Используйте файл-флаг. Рядом с данными отправитель кладёт пустой выгрузка.ready. Сначала проверяете флаг, потом читаете данные.
  3. ⏱ Сравнивайте размер и время изменения при двух запусках. Если за несколько минут они не изменились, файл дописан.

Не крутите цикл ожидания внутри одной процедуры. Встроенного «сна» на сервере нет, и пустой цикл просто нагрузит процессор. Удобнее регламентное задание, которое запускается раз в 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 используются HTTPСоединение и FTPСоединение.
Чем отличается КаталогВременныхФайлов() на клиенте и на сервере?
Это разные папки на разных компьютерах. Путь, полученный на сервере, бесполезен на клиенте, и наоборот.
Как проверить, что файл уже помещён во временное хранилище?
Объект Файл здесь не поможет: хранилище не лежит на диске. Проверьте адрес функцией ЭтоАдресВременногоХранилища() и получите данные через ПолучитьИзВременногоХранилища().