В процессе разработки расширений или доработки типовых конфигураций 1С часто возникает задача автоматического переноса вложенных файлов из одного документа в другой (например, из «Приобретения товаров и услуг» в созданную на его основе «Заявку на расходование денежных средств»). Однако при обычном копировании свойств элементов справочника программным путем разработчики часто сталкиваются с критической ошибкой: «Не удалось открыть файл... по причине: двоичные данные файла были удалены». Рассмотрим подробнее, почему это происходит и как правильно реализовать копирование данных.
Для начала проанализируем ситуацию с точки зрения внутреннего устройства системы. В современных конфигурациях, построенных на базе Библиотеки стандартных подсистем (БСП), работа с файлами реализована по многослойной схеме. Файл не является просто реквизитом документа. Информация о нем распределена между несколькими объектами:
бит_кзн_ЗаявкаНаРасходованиеСредствПрисоединенныеФайлы или ПриобретениеТоваровУслугПрисоединенныеФайлы). Здесь хранятся только метаданные: имя, расширение, размер, автор и ссылка на владельца (документ).ДвоичныеДанныеФайлов: Именно здесь физически хранятся двоичные данные, если выбран способ хранения «В информационной базе». Запись в этом регистре жестко привязана к ссылке на элемент справочника из пункта 1.Проанализируем типичную ошибку: когда мы просто создаем новый элемент в справочнике-приемнике и заполняем его через ЗаполнитьЗначенияСвойств, мы копируем описание файла, но не его содержимое. Система создает «пустышку», которая ссылается на несуществующую запись в регистре сведений или на отсутствующий файл в томе под новым идентификатором.
Если вы решили идти по пути прямого манипулирования данными, как это сделала автор темы, то после создания записи в справочнике необходимо обязательно создать соответствующую запись в регистре сведений ДвоичныеДанныеФайлов. Выясним, как это выглядит в коде.
Предположим, у нас есть СсылкаНаСтарыйФайл и мы создали НовыйФайлОбъект. Рассмотрим пример кода для переноса данных:
// 1. Создаем элемент в справочнике-приемнике
НовыйФайлОбъект = Справочники.ЗаявкаНаРасходованиеСредствПрисоединенныеФайлы.СоздатьЭлемент();
ЗаполнитьЗначенияСвойств(НовыйФайлОбъект, СсылкаНаСтарыйФайл);
НовыйФайлОбъект.ВладелецФайла = ДокументПриемник;
НовыйФайлОбъект.УстановитьНовыйСсылку(); // Важно получить ссылку до записи для регистра
НоваяСсылка = НовыйФайлОбъект.ПолучитьСсылкуНового();
НовыйФайлОбъект.Записать();
// 2. Копируем двоичные данные из регистра
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДвоичныеДанныеФайлов.ХранилищеФайла
|ИЗ
| РегистрСведений.ДвоичныеДанныеФайлов КАК ДвоичныеДанныеФайлов
|ГДЕ
| ДвоичныеДанныеФайлов.Файл = &СтарыйФайл";
Запрос.УстановитьПараметр("СтарыйФайл", СсылкаНаСтарыйФайл);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
// Создаем запись в регистре для нового файла
МенеджерЗаписи = РегистрыСведений.ДвоичныеДанныеФайлов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Файл = НоваяСсылка;
МенеджерЗаписи.ХранилищеФайла = Выборка.ХранилищеФайла;
МенеджерЗаписи.Записать();
КонецЕсли;
Важный нюанс: Этот способ сработает только в том случае, если файлы хранятся в базе. Если файлы хранятся в томах на диске, этот код не поможет, так как в регистре будет пусто, а физический файл на диске не будет продублирован под новым именем (UUID).
Для того чтобы не зависеть от способа хранения (база или тома) и не нарушать целостность данных, правильнее использовать методы программного интерфейса БСП. Посмотрим на функции общего модуля РаботаСФайлами. Этот подход более универсален и безопасен при обновлениях конфигурации.
Рассмотрим по шагам, как это реализовать:
// Получаем двоичные данные исходного файла
ДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(СсылкаНаСтарыйФайл);
// Параметры для создания нового файла
ПараметрыФайла = Новый Структура;
ПараметрыФайла.Вставить("Автор", Пользователи.ТекущийПользователь());
ПараметрыФайла.Вставить("ИмяБезРасширения", СсылкаНаСтарыйФайл.Наименование);
ПараметрыФайла.Вставить("Расширение", СсылкаНаСтарыйФайл.Расширение);
// Добавляем файл к новому документу
НовыйФайлСсылка = РаботаСФайлами.ДобавитьФайл(
ДокументПриемник,
ДанныеФайла,
СсылкаНаСтарыйФайл.Наименование,
"Копия при создании на основании",
ПараметрыФайла
);
Преимущество этого метода в том, что система сама определит, куда положить данные (в базу или в том), создаст все необходимые записи в служебных регистрах и корректно заполнит реквизиты справочника присоединенных файлов.
Проанализируем ситуацию, упомянутую в сообщении. До определенного момента в некоторых конфигурациях данные могли храниться непосредственно в реквизите справочника (типа ХранилищеЗначения). В новых версиях БСП разработчики 1С практически полностью перешли на вынос бинарного контента в отдельный регистр ДвоичныеДанныеФайлов. Это сделано для оптимизации: при построении списков или поиске по справочнику системе не нужно «прокачивать» через память тяжелые двоичные данные. Именно поэтому код, который просто копирует ЭлементСправочника, теперь создает битую ссылку на данные.
При копировании файлов между документами помните о следующих правилах:
ДвоичныеДанныеФайлов. Без записи в нем файл в интерфейсе будет виден, но не откроется.РаботаСФайлами инкапсулируют в себе всю сложную логику и гарантируют работоспособность кода после обновлений.Соблюдение этих рекомендаций позволит вам избежать ошибок при работе с файлами и обеспечит сохранность данных ваших пользователей.