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