Как исправить ошибку «Файл не обнаружен» при выгрузке данных в DBF на сервере 1С?

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

Работа с файлами формата DBF (через встроенный объект XBase) в клиент-серверном варианте работы платформы «1С:Предприятие» имеет ряд важных особенностей. Часто разработчики сталкиваются с ситуацией, когда код, успешно работающий на локальном компьютере в файловой базе, отказывается работать на сервере, возвращая ошибку вида «Файл не обнаружен» или ошибку совместного доступа.

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

Анализ исходной ситуации и причин возникновения ошибки

Рассмотрим классический пример кода, который часто приводит к сбою на сервере под управлением операционной системы Linux (на что указывает характерный путь к временному каталогу /tmp/):


&НаСервере
Функция Команда2НаСервере()
    НашеИмяФайла = "PriceP.dbf";
    НашПолныйПутьФайла = КаталогВременныхФайлов() + НашеИмяФайла;
    
    БД = Новый XBase;
    // (Здесь инициализируется структура колонок файла)
    БД.СоздатьФайл(НашПолныйПутьФайла);
    
    // Заполнение файла данными в цикле
    Пока Выборка.Следующий() Цикл
        БД.Добавить();
        БД.Цена = Выборка.Цена;
        БД.Записать();
    КонецЦикла;
    
    // Попытка поместить файл во временное хранилище
    АдресВХ = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(НашПолныйПутьФайла));
    Возврат АдресВХ;
КонецФункции

При выполнении этого кода на платформе Linux сервер возвращает ошибку:

Ошибка при вызове конструктора (ДвоичныеДанные): Файл не обнаружен '/tmp/PriceP.dbf'

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

Причина 1. Блокировка файла операционной системой (Отсутствие закрытия файла)

Объект XBase взаимодействует с файловой системой на низком уровне. Когда мы вызываем метод СоздатьФайл(), операционная система открывает дескриптор файла и предоставляет процессу монопольный доступ для записи.

До тех пор, пока мы явно не вызовем метод ЗакрытьФайл(), буферы записи операционной системы могут быть не сброшены на диск окончательно, а сам файл остается заблокированным рабочим процессом 1С (rphost). При попытке инициализировать объект Новый ДвоичныеДанные(ПутьКФайлу) платформа пытается прочитать этот файл с диска, но получает отказ операционной системы или читает пустой/недозаписанный файл. Метод ЗакрытьФайл() является обязательным завершением работы с базой данных dBase.

Причина 2. Регистрозависимость Linux и особенности работы XBase

Если сервер 1С:Предприятия работает под управлением ОС Linux, критически важной становится чувствительность файловой системы к регистру символов (case-sensitivity).

Платформа 1С имеет документированную особенность: при создании файлов формата DBF через объект XBase в среде Linux, расширение создаваемого файла принудительно приводится к верхнему регистру — .DBF, даже если в коде было написано .dbf с маленькой буквы.

Проанализируем этот момент:

  1. В коде мы передаем путь: /tmp/PriceP.dbf.
  2. Метод БД.СоздатьФайл() создает на диске файл с именем: /tmp/PriceP.DBF.
  3. Далее мы вызываем конструктор: Новый ДвоичныеДанные("/tmp/PriceP.dbf").
  4. Файловая система Linux ищет файл строго с маленьким расширением .dbf, не находит его и возвращает ошибку «Файл не обнаружен».

Для решения этой проблемы необходимо всегда контролировать регистр расширения при работе на Linux-серверах либо использовать встроенные механизмы генерации путей.

Причина 3. Конкурентный доступ в многопользовательской среде

Использование статического имени файла (например, "PriceP.dbf") в общем каталоге временных файлов — это грубая архитектурная ошибка. Если два пользователя одновременно запустят выгрузку прайса, возникнет конфликт совместного доступа к одному и тому же файлу в каталоге /tmp/. Один пользователь заблокирует файл, а второй получит системную ошибку.

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

Пошаговое руководство по правильной реализации выгрузки

Разберем эталонный и безопасный шаблон выгрузки данных в формат DBF в клиент-серверном режиме работы 1С:Предприятия.

Шаг 1. Генерируем уникальный путь к файлу на сервере

Для генерации уникального имени файла воспользуемся системной функцией. При этом учитываем требования к длине имен в формате dBase (желательно укладываться в стандарт DOS 8.3 для совместимости со старым ПО, хотя сама платформа 1С поддерживает длинные имена файлов).

Шаг 2. Корректно описываем структуру таблицы и создаем файл

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

Шаг 3. Заполняем, записываем и обязательно закрываем файл

После завершения цикла записи вызываем методы Записать() и ЗакрытьФайл().

Шаг 4. Помещаем файл во временное хранилище и удаляем его с сервера

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

Посмотрим на пример правильной реализации серверной функции:


&НаСервере
Функция СформироватьПрайсDBFНаСервере()
    
    // Шаг 1. Получаем уникальный путь к файлу с расширением в верхнем регистре
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("DBF");
    
    БД = Новый XBase;
    
    // Шаг 2. Обязательно определяем структуру колонок таблицы
    БД.Поля.Добавить("CODE", "S", 10);
    БД.Поля.Добавить("NAME", "S", 100);
    БД.Поля.Добавить("PRICE", "N", 15, 2);
    БД.Поля.Добавить("DATE", "D");
    
    // Создаем пустой файл со структурой
    БД.СоздатьФайл(ИмяВременногоФайла);
    
    Если Не БД.Открыта() Тогда
        ВызватьИсключение "Не удалось создать файл DBF на сервере!";
    КонецЕсли;
    
    // Шаг 3. Заполняем таблицу данными
    // Для примера выберем условные данные
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Код КАК Код,
        |   Номенклатура.Наименование КАК Наименование,
        |   ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |   ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныНоменклатурыСрезПоследних
        |       ПО ЦеныНоменклатурыСрезПоследних.Номенклатура = Номенклатура.Ссылка";
        
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        БД.Добавить();
        БД.CODE  = СокрЛП(Выборка.Код);
        БД.NAME  = Лев(СокрЛП(Выборка.Наименование), 100);
        БД.PRICE = Выборка.Цена;
        БД.DATE  = ТекущаяДата();
        БД.Записать();
    КонецЦикла;
    
    // Обязательно закрываем файл для снятия монопольной блокировки ОС
    БД.ЗакрытьФайл();
    
    // Шаг 4. Помещаем файл во временное хранилище
    АдресВХ = "";
    Попытка
        ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
        АдресВХ = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);
        
        // Удаляем временный файл с диска сервера, так как он больше не нужен
        УдалитьФайлы(ИмяВременногоФайла);
    Исключение
        // В случае ошибки гарантируем удаление временного файла
        УдалитьФайлы(ИмяВременногоФайла);
        ВызватьИсключение КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
    
    Возврат АдресВХ;
    
КонецФункции

Теперь рассмотрим, как правильно получить этот файл на стороне клиента и предложить пользователю сохранить его на локальный диск. Начиная с платформы 8.3, для интерактивного сохранения файлов рекомендуется использовать асинхронные вызовы:


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

Подведем итоги

Для стабильной работы с DBF-файлами в клиент-серверной архитектуре (особенно на кроссплатформенных серверах 1С под управлением Linux) всегда соблюдайте пять золотых правил:

  1. Всегда закрывайте файл методом ЗакрытьФайл() сразу после окончания записи или чтения.
  2. Передавайте файлы между клиентом и сервером исключительно через Временное хранилище. Платформа сама позаботится о репликации двоичных данных между нодами кластера серверов 1С.
  3. Используйте ПолучитьИмяВременногоФайла() для предотвращения конфликтов совместного доступа при одновременной работе нескольких пользователей.
  4. Учитывайте регистр расширения файлов в Linux (передавайте расширение в метод генерации временного имени как "DBF" в верхнем регистре).
  5. Своевременно удаляйте временные файлы с диска сервера с помощью метода УдалитьФайлы() в блоке Попытка...Исключение, чтобы избежать переполнения системных дисков.
← На главную