Как проверить, существует ли файл в 1С, и записать его под уникальным именем?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При программной выгрузке отчетов, печатных форм или других документов во внешние файлы разработчики часто сталкиваются с важной задачей: как не допустить случайного перезаписывания уже существующего файла? Если бухгалтер или менеджер регулярно выгружает документ с одинаковым стандартным именем, программа должна уметь определять конфликт имен и предлагать решение — например, сохранять новый файл с автоматическим добавлением цифрового индекса (вроде Отчет (1).xlsx, Отчет (2).xlsx).

В этой статье мы разберем по шагам, как проверить существование файла на диске, как реализовать асинхронную проверку в управляемом клиенте и как правильно генерировать красивые уникальные имена файлов с помощью встроенных средств 1С и библиотеки БСП.

Синхронная проверка существования файла на сервере

Начнем с самого простого сценария — выполнение кода на стороне сервера или в обычном приложении, где нет ограничений на использование синхронных вызовов. Для работы с файловой системой в 1С предназначен объект Файл. Мы можем инициализировать его, передав в конструктор полный путь к проверяемому файлу, а затем вызвать метод Существует().

Проанализируем простейший алгоритм, который в цикле ищет свободное имя файла, добавляя к нему порядковый номер, пока не найдет еще не занятый путь:


// Пример синхронной генерации свободного имени файла на сервере
ПутьККаталогу = "C:\Выгрузка\";
БазовоеИмя   = "РеестрДокументов";
Расширение   = ".pdf";

НомерФайла = 0;
ИмяФайлаВыгрузки = ПутьККаталогу + БазовоеИмя + Расширение;
ПроверяемыйФайл = Новый Файл(ИмяФайлаВыгрузки);

Пока ПроверяемыйФайл.Существует() Цикл
    НомерФайла = НомерФайла + 1;
    ИмяФайлаВыгрузки = ПутьККаталогу + БазовоеИмя + " (" + НомерФайла + ")" + Расширение;
    ПроверяемыйФайл = Новый Файл(ИмяФайлаВыгрузки);
КонецЦикла;

// На выходе получаем гарантированно свободный путь для записи
ЗаписатьПечатнуюФорму(ИмяФайлаВыгрузки);

Этот метод отлично работает на стороне сервера (например, в фоновом задании), но при работе непосредственно на клиенте в управляемых формах использование синхронных методов работы с файлами крайне не рекомендуется, а на некоторых конфигурациях и вовсе заблокировано платформой для предотвращения «зависания» интерфейса пользователя.

Асинхронные вызовы на клиенте в управляемом приложении

Если проверка должна выполняться непосредственно на компьютере пользователя в тонком или веб-клиенте, нам необходимо использовать асинхронные методы. Рассмотрим подробнее два основных способа реализации такой проверки.

Способ 1. Использование описания оповещения (для старых версий платформы)

Этот подход основан на создании специального объекта ОписаниеОповещения, который указывает платформе, какую процедуру нужно вызвать после того, как завершится проверка файла операционной системой. Посмотрим на практический пример:


&НаКлиенте
Процедура ПроверитьФайлПередЗаписью(ИмяФайла)
    Файл = Новый Файл(ИмяФайла);
    
    // Описываем процедуру обратного вызова, которая получит результат
    Оповещение = Новый ОписаниеОповещения("ПослеПроверкиСуществования", ЭтотОбъект, ИмяФайла);
    
    // Запускаем асинхронную проверку
    Файл.НачатьПроверкуСуществования(Оповещение);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПроверкиСуществования(Существует, ДополнительныеПараметры) Экспорт
    ИмяФайла = ДополнительныеПараметры;
    
    Если Существует Тогда
        ПоказатьПредупреждение(, "Файл с именем " + ИмяФайла + " уже существует! Выберите другое имя.");
    Иначе
        // Выполняем запись файла
        ЗаписатьДанныеВФайл(ИмяФайла);
    EndIf;
КонецПроцедуры

Способ 2. Современный асинхронный подход с Асинх/Ждать (Платформа 8.3.18+)

В современных версиях платформы «1С:Предприятие 8» разработчикам доступен гораздо более удобный механизм асинхронности, основанный на обещаниях (Promises). Он позволяет писать асинхронный код в линейном, привычном стиле без создания дополнительных процедур-колбэков. Для этого используются ключевые слова Асинх для объявления процедуры и Ждать для ожидания результата.

Выясним, как выглядит современная проверка файла на клиенте:


&НаКлиенте
Асинх Процедура ПроверитьИЗаписатьФайлАсинх(ПолныйПуть)
    Файл = Новый Файл(ПолныйПуть);
    
    // Программа временно приостановит выполнение процедуры, не блокируя интерфейс пользователя
    Существует = Ждать Файл.СуществуетАсинх();
    
    Если Существует Тогда
        // Выполняем логику переименования или запрашиваем подтверждение перезаписи
        ПоказатьПредупреждение(, "Внимание! Файл будет перезаписан.");
    КонецЕсли;
    
    ЗаписатьДанныеВФайл(ПолныйПуть);
КонецПроцедуры

Автоматическая генерация «человекочитаемого» имени файла

Просто сообщить пользователю о том, что файл существует — это лишь половина дела. Намного удобнее сразу предложить ему готовое уникальное имя. Использование временных имен (вроде тех, что возвращает функция ПолучитьИмяВременногоФайла()) не всегда подходит, поскольку пользователям неудобно работать со случайными наборами символов.

Рассмотрим, как красиво добавить индекс к имени файла, сохранив его расширение.

Вариант А. С использованием Библиотеки стандартных подсистем (БСП)

Если вы ведете разработку в типовой конфигурации 1С (такой как БП, ERP, КА, УТ, ЗУП), то писать собственные велосипеды для разбора строки пути не требуется. В БСП есть готовый оптимизированный функционал, который автоматически подберет свободное имя, добавив к нему порядковый номер в скобках.

На клиенте мы можем использовать следующий вызов:


// Функция вернет уникальный путь, например, "C:\Reports\Реестр (1).xlsx"
УникальныйПуть = ФайловаяСистемаКлиент.УникальноеИмяФайла("C:\Reports\Реестр.xlsx");

На сервере доступен аналогичный метод:

УникальныйПуть = ФайловаяСистема.УникальноеИмяФайла("C:\Reports\Реестр.xlsx");

Вариант Б. Ручной алгоритм разбора пути (без использования БСП)

Если разработка ведется на самописной конфигурации, где БСП отсутствует, нам придется написать собственную функцию. Главная сложность здесь заключается в том, чтобы правильно отделить расширение файла от его имени, иначе при простом сложении строк мы рискуем получить некорректные имена вида Отчет.xlsx(1) вместо правильного Отчет(1).xlsx.

Разберем пример такой функции:


Функция ПолучитьУникальноеИмяФайла(ПолноеИмя) Экспорт
    ПроверяемыйФайл = Новый Файл(ПолноеИмя);
    
    // Если файл не существует, то исходное имя уже уникально
    Если Не ПроверяемыйФайл.Существует() Тогда
        Возврат ПолноеИмя;
    КонецЕсли;
    
    // Разбиваем путь на составляющие компоненты
    КаталогФайла     = ПроверяемыйФайл.Путь;
    ИмяБезРасширения = ПроверяемыйФайл.ИмяБезРасширения;
    Расширение       = ПроверяемыйФайл.Расширение; // Содержит точку, например, ".xlsx"
    
    Счетчик = 1;
    Пока Истина Цикл
        // Формируем новое потенциальное имя с индексом в скобках
        НовоеИмя = КаталогФайла + ИмяБезРасширения + " (" + Счетчик + ")" + Расширение;
        ВременныйФайл = Новый Файл(НовоеИмя);
        
        Если Не ВременныйФайл.Существует() Тогда
            Возврат НовоеИмя;
        КонецЕсли;
        
        Счетчик = Счетчик + 1;
    КонецЦикла;
КонецФункции

Обработка непредвиденных ошибок при записи (Блокировки ОС)

Важно помнить, что даже если проверка Файл.Существует() вернула ложь, это все еще не гарантирует стопроцентный успех последующей записи. Проанализируем типичные ситуации, способные вызвать критическую ошибку выполнения кода:

  1. Файл с таким именем открыт другим пользователем в локальной сети или заблокирован процессом операционной системы (например, открыт в программе Excel).
  2. У текущего пользователя операционной системы нет прав на запись данных в указанный каталог.
  3. Указанный путь содержит недопустимые символы или слишком длинное имя каталога.

Чтобы избежать аварийного завершения работы программы и зависания сеанса пользователя, любую операцию записи во внешние файлы необходимо обязательно оборачивать в защитную конструкцию Попытка ... Исключение:


Попытка
    // Попытка непосредственного сохранения табличного документа на диск
    ТабличныйДокумент.Записать(ИмяФайлаВыгрузки, ТипФайлаТабличногоДокумента.XLSX);
Исключение
    // Обработка ошибки доступа к файловой системе
    ТекстОшибки = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    ВызватьИсключение "Не удалось записать файл на диск! Возможно, он открыт в другой программе. Подробности: " + ТекстОшибки;
КонецПопытки;

Специфика работы в веб-клиенте

Если ваши пользователи работают с базой данных 1С через интернет-браузер (Веб-клиент), то прямая работа с локальной файловой системой компьютера по соображениям безопасности браузеров полностью заблокирована. Команда Новый Файл() не сможет проверить файлы на локальном диске пользователя напрямую без установленного расширения работы с файлами.

Для веб-клиента стандартным и наиболее надежным паттерном работы считается:

  1. Формирование файла отчета или печатной формы на стороне сервера.
  2. Помещение готового двоичного макета во Временное хранилище и передача его адреса на клиент.
  3. Интерактивный вызов метода ПолучитьФайлССервера(), который инициирует стандартный системный диалог скачивания браузера. Пользователь сам выбирает конечный каталог и решает проблему дублирования имен средствами своей операционной системы.

Использование описанных методик позволит вам создавать надежные, современные и дружелюбные к пользователю механизмы интеграции 1С с файловой системой вашего компьютера.

← На главную