Рассмотрим частую задачу: необходимо массово выгрузить изображения номенклатуры (или другие присоединенные файлы) из информационной базы 1С в каталог на жестком диске. Многие программисты при написании обработки обращаются напрямую к реквизитам справочника с присоединенными файлами, но сталкиваются с тем, что вместо содержимого файла возвращается значение Неопределено. Проанализируем причину такого поведения и разберем по шагам, как правильно реализовать выгрузку в современных конфигурациях (таких как УНФ, УТ 11, ERP, КА и других решениях, построенных на базе Библиотеки стандартных подсистем — БСП).
В первоначальном подходе разработчик пытается запросом получить данные из реквизита ФайлХранилище (тип ХранилищеЗначения) из справочника присоединенных файлов и затем применить метод Получить(). Однако в этот момент возвращается пустое значение. Почему так происходит?
Дело в том, что в конфигурациях с внедренной БСП реализовано два механизма физического хранения прикрепленных файлов:
Если в настройках программы установлена галочка «Хранить файлы в томах на диске», то реквизит, отвечающий за хранилище внутри базы, будет пустым (именно поэтому система возвращает Неопределено). Чтобы универсально получать файлы независимо от места их физического хранения, мы должны использовать специализированные процедуры и функции общих модулей БСП.
Для работы с файлами в БСП предусмотрены общие модули, такие как РаботаСФайлами или ПрисоединенныеФайлы. Они инкапсулируют логику поиска файла: проверяют, где он лежит (в базе или в томе), считывают его и возвращают готовые двоичные данные.
Для получения двоичных данных картинки по ссылке на присоединенный файл отлично подходит функция РаботаСФайлами.ДвоичныеДанныеФайла(). Рассмотрим готовый пример кода для пакетной выгрузки картинок.
&НаСервере
Процедура ВыгрузитьКартинкиНаСервере(КаталогВыгрузки)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| НоменклатураПрисоединенныеФайлы.Расширение КАК Расширение,
| Номенклатура.Артикул КАК Артикул,
| НоменклатураПрисоединенныеФайлы.Ссылка КАК СсылкаНаФайл
|ИЗ
| Справочник.НоменклатураПрисоединенныеФайлы КАК НоменклатураПрисоединенныеФайлы
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО НоменклатураПрисоединенныеФайлы.ВладелецФайла = Номенклатура.Ссылка
|ГДЕ
| НЕ НоменклатураПрисоединенныеФайлы.ВладелецФайла ЕСТЬ NULL
| И НоменклатураПрисоединенныеФайлы.Расширение В (""jpg"", ""png"", ""jpeg"")";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем двоичные данные через БСП
ДвоичныйФайл = РаботаСФайлами.ДвоичныеДанныеФайла(Выборка.СсылкаНаФайл, Ложь);
Если ДвоичныйФайл = Неопределено Тогда
Продолжить; // Файл физически не найден
КонецЕсли;
// Формируем имя файла (Артикул.Расширение)
ИмяФайла = Выборка.Артикул + "." + Выборка.Расширение;
ПолныйПуть = КаталогВыгрузки + "\" + ИмяФайла;
// Записываем файл на диск
ДвоичныйФайл.Записать(ПолныйПуть);
КонецЦикла;
КонецПроцедуры
Хотя приведенный выше код полностью решает базовую задачу, при реальной эксплуатации обработки выгрузки следует учитывать несколько важных моментов.
1. Контекст выполнения (Клиент-серверная архитектура)
Обратите внимание, что метод Записать() вызывается в процедуре с директивой &НаСервере. Это означает, что если переменная КаталогВыгрузки содержит путь вида C:\Pic\, то файлы будут сохранены на локальный диск самого сервера 1С (компьютера, где работает служба Агента сервера 1С). Пользователь, запустивший обработку со своего рабочего компьютера, этих файлов у себя на диске C:\ не увидит.
Если стоит задача сохранить файлы именно на компьютер пользователя, необходимо на сервере получать двоичные данные, помещать их во временное хранилище (с помощью функции ПоместитьВоВременноеХранилище()), возвращать адрес на клиент, и уже &НаКлиенте вызывать методы НачатьПолучениеФайлаССервера() или НачатьПолучениеФайловССервера() для сохранения на локальный диск пользователя. Для организации передачи данных между сторонами подойдёт обработка для копирования файлов и каталогов между клиентом и сервером.
2. Выборка: только основная картинка или все?
В запросе мы соединяем справочник присоединенных файлов с номенклатурой по условию НоменклатураПрисоединенныеФайлы.ВладелецФайла = Номенклатура.Ссылка. Это означает, что если у одного товара пять картинок (ракурсы, упаковка и т.д.), код попытается выгрузить их все. Если при этом имя файла формируется только из артикула (Выборка.Артикул + "." + Выборка.Расширение), файлы одного и того же товара будут перезаписывать друг друга.
Чтобы этого избежать, добавляйте к имени файла уникальный идентификатор или счетчик.
Если же вам нужна только основная (главная) картинка номенклатуры, измените условие соединения в запросе на: ПО НоменклатураПрисоединенныеФайлы.Ссылка = Номенклатура.ФайлКартинки.
3. Фильтрация по расширению
В присоединенных файлах к номенклатуре менеджеры могут хранить не только фотографии. Там могут оказаться PDF-инструкции, сертификаты в формате Word (.docx) и прайс-листы Excel. Если ваша цель — только изображения для сайта, обязательно накладывайте в запросе фильтр на реквизит Расширение (например, В ("jpg", "png", "jpeg")), как мы это сделали в итоговом примере кода.
4. Недопустимые символы в именах файлов
При формировании имени файла из реквизитов базы (например, из Наименования или Артикула) помните, что в них могут содержаться символы, запрещенные файловой системой Windows (\ / : * ? " < > |). Если такой символ встретится в строке адреса, метод Записать() выдаст критическую ошибку. Перед формированием пути обязательно очищайте строку от этих символов (например, с помощью функции СтрЗаменить()).