В процессе разработки часто возникает задача реализации галереи изображений для конкретного объекта — например, карточки товара или сотрудника. Начинающие разработчики часто сталкиваются с трудностями при попытке сохранить несколько файлов в табличную часть и корректно отобразить их на форме. Рассмотрим подробно, как решить эту задачу, проанализируем типичные ошибки и разберем архитектурно правильные подходы.
Рассмотрим ситуацию, когда разработчик создает в справочнике табличную часть Фотографии с реквизитом ДвоичныеДанныеФото (тип ХранилищеЗначения). Основная проблема в коде автора заключалась в неправильной логике обхода коллекции при записи данных. Разберем типичную ошибку в процедуре ПередЗаписьюНаСервере:
// ОШИБОЧНЫЙ ВАРИАНТ
Если СписокФото.Количество > 0 Тогда;
НоваяСтрока = ТекущийОбъект.Фотографии.Добавить(); // Строка добавляется ОДИН раз вне цикла
Для Элемент = 0 по СписокФото.Количество - 1 Цикл
НоваяСтрока.ДвоичныеДанныеФото = ПолучитьИзВременногоХранилища(СписокФото[Элемент]); // Каждая итерация затирает предыдущее значение
КонецЦикла;
КонецЕсли;
В данном примере в табличную часть всегда записывается только одно изображение (последнее из списка), так как метод Добавить() вызывается до начала цикла. Выясним, как написать этот фрагмент правильно, чтобы каждая фотография занимала свою строку в таблице.
Для корректного сохранения всех выбранных файлов нам необходимо создавать новую строку табличной части внутри цикла обхода загруженных данных. Рассмотрим исправленный программный код:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверяем, есть ли новые фото во временном хранилище (СписокФото)
Если СписокФото.Количество() > 0 Тогда
// Если нужно заменить старые фото новыми, очищаем табличную часть
ТекущийОбъект.Фотографии.Очистить();
Для Каждого ЭлементСписка Из СписокФото Цикл
// Создаем НОВУЮ строку для каждого файла
НоваяСтрока = ТекущийОбъект.Фотографии.Добавить();
// Помещаем двоичные данные из временного хранилища в ХранилищеЗначения
НоваяСтрока.ДвоичныеДанныеФото = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(ЭлементСписка.Значение));
КонецЦикла;
// Очищаем список на форме, чтобы избежать дублирования при повторной записи
СписокФото.Очистить();
КонецЕсли;
КонецПроцедуры
Обратите внимание на использование объекта Новый ХранилищеЗначения. В реквизиты табличной части с типом ХранилищеЗначения данные должны попадать именно в упакованном виде, а не как «сырые» двоичные данные.
После того как данные успешно сохранены в базу, их необходимо показать пользователю. В 1С Поле картинки на форме работает со строковыми навигационными ссылками. Проблема в том, что для табличной части ссылка формируется сложнее, чем для обычного реквизита. Проанализируем, как получить ссылку на конкретную строку:
Стандартный синтаксис функции ПолучитьНавигационнуюСсылку для табличной части выглядит так: ПолучитьНавигационнуюСсылку(ОбъектСсылка, "ИмяТабличнойЧасти.ИмяРеквизита", ИндексСтроки).
Однако, использование индекса строки (0, 1, 2...) — не самый надежный метод, так как при сортировке или удалении строк индексы меняются. Рассмотрим более универсальный способ: вывод списка фотографий через таблицу на форме.
Объект.Фотографии.ДвоичныеДанныеФото, а с вычисляемым путем (навигационной ссылкой).ПриАктивизацииСтроки будем обновлять основное ПолеКартинки, установленное на форме.
&НаКлиенте
Процедура ФотографииПриАктивизацииСтроки(Элемент)
ТекущиеДанные = Элементы.Фотографии.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
// Формируем ссылку на конкретную строку по уникальному идентификатору (ID) строки
Фото = ПолучитьНавигационнуюСсылку(Объект.Ссылка, "Фотографии.ДвоичныеДанныеФото", ТекущиеДанные.НомерСтроки - 1);
КонецЕсли;
КонецПроцедуры
Несмотря на работоспособность метода с табличной частью, в реальных высоконагруженных системах так делать не рекомендуется. Разберем причины:
ПолучитьОбъект()) система считывает из БД все данные, включая табличные части. Если у вас в ТЧ лежит 10 фотографий по 5 Мб каждая, то простое переименование элемента справочника в коде заставит сервер «прокачать» через память 50 Мб лишних данных.Правильным решением будет использование присоединенных файлов. Рассмотрим, как это реализовано в Библиотеке стандартных подсистем (БСП):
ИмяВашегоСправочникаПрисоединенныеФайлы.Если вы не используете БСП, можно реализовать упрощенный аналог. Создадим подчиненный справочник ФайлыОбъектов с владельцем — вашим основным справочником. В нем создадим реквизит ФайлХранилище (тип ХранилищеЗначения).
Теперь, чтобы отобразить галерею, мы разместим на форме основного объекта Динамический список, указывающий на справочник ФайлыОбъектов с отбором по владельцу. Это позволит:
Для создания действительно качественного интерфейса рассмотрим технику «превью». Проанализируем ситуацию: пользователю нужно увидеть список из 20 фото, чтобы выбрать нужное. Загружать 20 полноразмерных фото долго.
Алгоритм оптимизации:
Для сжатия изображений в 1С можно использовать объект Картинка и метод Преобразовать(), либо воспользоваться внешними компонентами или библиотеками, если требуется специфическое качество сжатия.
Таким образом, мы разобрали путь от исправления простой ошибки в цикле до построения грамотной архитектуры хранения медиафайлов. Выбор метода зависит от ваших задач: для учебной базы или очень маленького количества фото подойдет табличная часть, но для серьезных проектов всегда выбирайте механизм присоединенных файлов.