При разработке расширений или модификации типовых конфигураций на платформе 1С:Предприятие 8 часто возникает задача автоматического переноса вложенных файлов. Например, когда на основании документа Приобретение товаров и услуг создается Заявка на расходование денежных средств, логично ожидать, что все счета, акты и другие сканы документов «перекочуют» в новый объект. Однако обычное копирование записей в справочнике присоединенных файлов часто приводит к ошибке: «двоичные данные файла были удалены».
В этой статье мы подробно разберем, почему так происходит, как устроена современная архитектура хранения файлов в Библиотеке стандартных подсистем (БСП) и какие способы решения проблемы существуют.
Рассмотрим ситуацию, с которой сталкиваются многие разработчики. Допустим, мы решили просто создать новую запись в справочнике присоединенных файлов, скопировав значения реквизитов из старой записи. Типичный код выглядит примерно так:
НоваяСтрокаСправочника = Справочники.бит_кзн_ЗаявкаНаРасходованиеСредствПрисоединенныеФайлы.СоздатьЭлемент();
ЗаполнитьЗначенияСвойств(НоваяСтрокаСправочника, СтрокаТЗ); // СтрокаТЗ - данные исходного файла
НоваяСтрокаСправочника.ВладелецФайла = ДокументПланирования;
НоваяСтрокаСправочника.Записать();
На первый взгляд всё верно: мы создали элемент справочника, указали нового владельца и сохранили объект. Однако при попытке открыть такой файл пользователь увидит сообщение об ошибке. Причина кроется в том, что в современных конфигурациях (ERP, КА, УТ 11, БП 3.0) справочник присоединенных файлов — это лишь «верхушка айсберга» или метаданные. Сами двоичные данные (тело файла) хранятся отдельно.
Чтобы понять, как правильно копировать файлы, проанализируем структуру их хранения. БСП использует многоуровневую схему:
ДоговорыКонтрагентовПрисоединенныеФайлы). Здесь хранится наименование файла, ссылка на владельца (документ или справочник), информация о создании и флаг наличия данных.ДвоичныеДанныеФайлов. Если в настройках системы выбрано хранение в информационной базе, то именно в этом регистре, в ресурсе с типом ХранилищеЗначений, лежат байты вашего документа.Проанализируем ошибку автора: при копировании только элемента справочника (шаг 1) система создает запись-пустышку. Она не знает, где искать тело файла, так как записи в регистре ДвоичныеДанныеФайлов или соответствующей версии не существует.
Если нам необходимо быстро решить проблему «здесь и сейчас» и мы точно знаем, что файлы хранятся в базе, а не на томах, можно дополнить код прямой записью в регистр. После того как мы записали элемент справочника присоединенных файлов, нам нужно найти исходные данные и перенести их.
Разберем алгоритм действий:
Пример реализации:
// Создаем карточку нового файла
НовыйФайл = Справочники[ИмяСправочникаПрисоединенныхФайлов].СоздатьЭлемент();
ЗаполнитьЗначенияСвойств(НовыйФайл, ДанныеИсточника);
НовыйФайл.ВладелецФайла = НовыйВладелец;
НовыйФайл.Записать();
// Переносим двоичные данные в регистр
МенеджерЗаписи = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Файл = НовыйФайл.Ссылка; // Или ссылка на версию, зависит от версии БСП
// Получаем данные старого файла
ДанныеСтарогоФайла = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьМенеджерЗаписи();
ДанныеСтарогоФайла.Файл = СтараяСсылкаНаФайл;
ДанныеСтарогоФайла.Прочитать();
Если ДанныеСтарогоФайла.Выбран() Тогда
МенеджерЗаписи.ДвоичныеДанные = ДанныеСтарогоФайла.ДвоичныеДанные;
МенеджерЗаписи.Записать();
КонецЕсли;
Важно: В зависимости от версии БСП, измерением в регистре ДвоичныеДанныеФайлов может выступать либо сам «Присоединенный файл», либо «Версия файла». Проверьте это в конфигураторе перед написанием кода.
Разберем более правильный и надежный подход. Прямая работа с регистрами опасна тем, что при обновлении платформы или конфигурации логика может измениться (например, вы перейдете на хранение в томах). Правильнее использовать методы общего модуля РаботаСФайлами.
Проанализируем метод СкопироватьФайл. Этот метод берет на себя всю рутину по созданию записей в справочниках версий, копированию данных в регистры или на дисковые тома, а также корректно прописывает GUID.
Рассмотрим пример использования:
// ИсходныйФайлСсылка - это ссылка на элемент справочника присоединенных файлов источника
// НовыйВладелец - ссылка на документ-приемник
ПараметрыКопирования = РаботаСФайлами.ПараметрыКопированияФайла();
ПараметрыКопирования.ВладелецФайла = НовыйВладелец;
НовыйФайлСсылка = РаботаСФайлами.СкопироватьФайл(ИсходныйФайлСсылка, ПараметрыКопирования);
Этот метод гарантирует, что файл будет открываться у пользователя без ошибок, так как система автоматически подтянет все необходимые зависимости.
Иногда копирование данных — это путь к неоправданному росту объема базы. Если вы копируете файлы из Заказа в Реализацию, а затем в Счет-фактуру, один и тот же тяжелый PDF-документ может занять в три раза больше места.
Выясним, можно ли избежать дублирования. Вместо физического копирования можно подменить список отображаемых файлов в форме документа-приемника. Для этого в динамическом списке, который выводит присоединенные файлы, можно изменить запрос так, чтобы он выбирал файлы не только текущего документа, но и документа-основания.
Рассмотрим шаги реализации:
ДинамическийСписок.ГДЕ ВладелецФайла В (&МассивВладельцев).МассивВладельцев передаем ссылку на текущий документ и ссылку на документ-основание.Этот способ экономит место и позволяет видеть историю файлов «сквозь» документы.
При ручном копировании (как в Решении 1) часто забывают про права доступа. В БСП за права на просмотр файлов отвечают отдельные механизмы (регистры прав). Если вы просто добавили запись в базу «в обход» стандартных методов, пользователь может получить ошибку доступа, даже если двоичные данные на месте.
Основные выводы:
ДвоичныеДанныеФайлов или томах на диске.РаботаСФайлами избавят вас от необходимости изучать структуру регистров.Разберем напоследок ситуацию с хранением в томах. Если ваша система настроена на внешнее хранение, то при программном создании файла необходимо убедиться, что файл физически скопирован в нужную папку на сервере 1С. Метод РаботаСФайлами.СкопироватьФайл() делает это автоматически, что делает его самым предпочтительным инструментом для разработчика.