Как программно найти и сохранить присоединенные файлы БСП по реквизиту документа

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

Работа с присоединенными файлами в конфигурациях, построенных на базе Библиотеки стандартных подсистем (БСП), существенно отличается от манипуляций с обычными справочниками. Это связано с тем, что БСП берет на себя управление хранением (в базе или в томах на диске), версионированием и правами доступа. Рассмотрим типичную задачу: необходимо найти все сканы накладных, прикрепленные к документам ПриходМатериалов, ориентируясь на строковый номер накладной, и выгрузить эти файлы в определенную папку на диске.

Анализируем структуру хранения файлов в БСП

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

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

Шаг 1: Формирование оптимального запроса для поиска файлов

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

Допустим, мы уже выгрузили список нужных номеров накладных в массив НомераНакладных. Теперь составим запрос к справочнику файлов, который связан с нашим документом. Выясним причину, по которой поиск может не давать результатов: часто реквизит связи называется ВладелецФайла, и он должен указывать на ссылку документа ПриходМатериалов.


// Подготовим номера накладных (из табличной части объекта)
ТЗ_Номера = Объект.СоставНакладных.Выгрузить(,"НомерНакладной");
ТЗ_Номера.Свернуть("НомерНакладной");
МассивНомеров = ТЗ_Номера.ВыгрузитьКолонку("НомерНакладной");

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Файлы.Ссылка КАК ФайлСсылка,
    |	Файлы.Наименование КАК ИмяФайла,
    |	Файлы.Расширение КАК Расширение,
    |	Файлы.ТекущаяВерсия КАК ТекущаяВерсия
    |ИЗ
    |	Справочник.ПриходМатериаловПрисоединенныеФайлы КАК Файлы
    |ГДЕ
    |	Файлы.ВладелецФайла.НомерНакладной В (&МассивНомеров)
    |	И НЕ Файлы.ПометкаУдаления";

Запрос.УстановитьПараметр("МассивНомеров", МассивНомеров);
РезультатЗапроса = Запрос.Выполнить();

Обратите внимание: мы обращаемся к Файлы.ВладелецФайла.НомерНакладной. Это допустимо, если ВладелецФайла имеет строго определенный тип. Если же тип составной, лучше использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей документа.

Шаг 2: Получение двоичных данных через API БСП

После того как мы получили ссылки на файлы, нам необходимо извлечь их содержимое. Рассмотрим использование метода РаботаСФайлами.ДвоичныеДанныеФайла. Этот метод универсален: он сам определит, лежит ли файл в томе на диске или в информационной базе, и вернет объект ДвоичныеДанные.

Проанализируем нюанс с версионированием. В БСП файлы могут иметь несколько версий. Реквизит ТекущаяВерсия справочника файлов указывает на самый актуальный вариант документа. Хотя метод ДвоичныеДанныеФайла часто принимает и ссылку на сам файл, надежнее передавать в него именно ссылку на версию.


Выборка = РезультатЗапроса.Выбрать();
КаталогВыгрузки = "C:\123\"; // Путь на сервере!

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

Разберем важные особенности реализации

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

  1. Контекст выполнения: Если вы вызываете ДвоичныеДанные.Записать() в серверном коде, файл будет сохранен на диске сервера. Если пользователь ожидает увидеть файл на своем рабочем столе, этот подход не сработает. В таком случае файлы нужно передавать на клиент и использовать ФайловаяСистемаКлиент.СохранитьФайл() или методы БСП из модуля РаботаСФайламиКлиент.
  2. Права доступа: БСП проверяет права доступа к файлам на основании прав к объекту-владельцу. Если у пользователя нет прав на просмотр документа ПриходМатериалов, запрос к справочнику файлов вернет пустой результат (из-за механизмов RLS).
  3. Очистка имен файлов: Имя файла в справочнике (реквизит Наименование) может содержать символы, недопустимые в операционной системе (например, слэши или двоеточия). Перед записью на диск рекомендуется "очищать" строку наименования.
  4. Дублирование имен: Если два разных документа имеют прикрепленные файлы с одинаковым именем (например, "скан.pdf"), при выгрузке в одну папку один файл перезапишет другой. Посмотрим на решение: стоит добавлять к имени файла префикс в виде номера накладной или уникального идентификатора.

Альтернативный метод: РаботаСФайлами.ПрисоединенныеФайлыОбъекта

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


СписокФайлов = РаботаСФайлами.ПрисоединенныеФайлыОбъекта(СсылкаНаДокумент);
Для Каждого ФайлСсылка Из СписокФайлов Цикл
    Данные = РаботаСФайлами.ДанныеФайла(ФайлСсылка);
    // Данные — это структура с информацией о файле (имя, расширение, ссылка на версию)
    // Далее получаем ДвоичныеДанные и сохраняем
КонецЦикла;

Этот метод предпочтительнее, так как он инкапсулирует в себе логику запросов к базе и гарантирует получение корректного набора данных согласно настройкам БСП.

Выясним причину возможных ошибок ("Данные не найдены")

Если код отрабатывает без ошибок, но РаботаСФайлами.ДвоичныеДанныеФайла возвращает Неопределено, проанализируем ситуацию:

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

← На главную