При разработке внешних обработок в системе 1С:Предприятие часто возникает задача по отображению динамически загружаемых изображений, например, из выбранного пользователем каталога. Стандартные механизмы платформы предоставляют инструментарий для решения этой задачи, однако требуют понимания особенностей работы с файлами, временным хранилищем и динамическим созданием элементов формы. В этой статье мы подробно рассмотрим, как программно получить файлы изображений, разместить их во временном хранилище и вывести на форму внешней обработки, а также обсудим важные нюансы, связанные с производительностью и использованием ресурсов.
Наш пользователь сталкивается с задачей, где необходимо выбрать каталог, найти в нем все файлы изображений (PNG, JPG, JPEG), а затем программно отобразить их на форме внешней обработки. Основные сложности заключаются в следующих моментах:
Хранение изображений: Во внешней обработке нет возможности создать реквизит типа "ХранилищеЗначений" напрямую. Следовательно, изображения необходимо хранить в виде двоичных данных и помещать во временное хранилище, используя эффективные инструменты для работы с двоичными данными.
Отображение на форме: Как программно создать реквизиты и элементы формы для каждой картинки, а затем привязать к ним адреса из временного хранилища?
Рассмотрим пошагово решение этой задачи.
Этот подход является наиболее распространенным и рекомендуемым для динамического вывода изображений в 1С. Он базируется на использовании серверных механизмов для работы с файлами и временным хранилищем, а затем связывании этих данных с элементами формы.
Предполагаем, что вы уже реализовали логику выбора каталога и отбора файлов изображений. В результате у вас должен быть массив путей к файлам, которые нужно загрузить. Для работы с файлами и временным хранилищем в 1С, особенно в управляемых формах, все операции с файловой системой и помещением данных в хранилище необходимо выполнять на сервере.
Мы будем использовать метод НачатьПомещениеФайловНаСервер для помещения файлов во временное хранилище. Этот метод является асинхронным и позволяет получить адреса размещенных файлов. Обратите внимание, что методы НачатьПомещениеФайла и НачатьПолучениеФайлов не рекомендуются к использованию.
Предположим, у нас есть процедура на клиенте, которая вызывает серверный метод для загрузки файлов:
// В обработчике команды на клиенте
&НаКлиенте
Процедура ЗагрузитьКартинки(Команда)
ДиалогВыбораКаталога = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Если ДиалогВыбораКаталога.Выбрать() Тогда
ВыбранныйКаталог = ДиалогВыбораКаталога.Каталог;
// Получаем список файлов изображений из каталога
МассивФайловДляЗагрузки = Новый Массив;
НайтиФайлыИзображенийВКаталоге(ВыбранныйКаталог, МассивФайловДляЗагрузки);
Если МассивФайловДляЗагрузки.Количество() > 0 Тогда
// Вызываем серверную процедуру для помещения файлов и создания элементов формы
ПоместитьФайлыИСоздатьЭлементы(МассивФайловДляЗагрузки);
Иначе
Сообщить("В выбранном каталоге не найдено файлов изображений.");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура НайтиФайлыИзображенийВКаталоге(Каталог, МассивФайлов)
Файлы = НайтиФайлы(Каталог, "*.*", Истина); // Ищем все файлы, включая подкаталоги
Для Каждого Файл Из Файлы Цикл
Расширение = СтрНижний(Файл.Расширение);
Если Расширение = "png" Или Расширение = "jpg" Или Расширение = "jpeg" Тогда
МассивФайлов.Добавить(Файл.ПолноеИмя);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Серверная процедура для помещения файлов во временное хранилище и создания элементов
&НаСервере
Процедура ПоместитьФайлыИСоздатьЭлементы(СписокПутейКФайлам)
// Очищаем существующие реквизиты и элементы перед повторной загрузкой
ОчиститьСуществующиеЭлементыИРеквизиты();
МассивДляПомещения = Новый Массив;
Для Каждого ПутьКФайлу Из СписокПутейКФайлам Цикл
ФайлДанных = Новый Файл(ПутьКФайлу);
Если ФайлДанных.Существует() Тогда
Файл = Новый ПомещаемыйФайл(ПутьКФайлу);
МассивДляПомещения.Добавить(Файл);
КонецЕсли;
КонецЦикла;
Если МассивДляПомещения.Количество() > 0 Тогда
// Помещаем файлы на сервер
РезультатПомещения = НачатьПомещениеФайловНаСервер(МассивДляПомещения, УникальныйИдентификатор);
СозданиеРеквизитовИЭлементовФормы(РезультатПомещения);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ОчиститьСуществующиеЭлементыИРеквизиты()
// ЭтотМетодНадоРеализоватьДляКорректнойПовторнойЗагрузки
// Например, можно хранить список созданных реквизитов и элементов
// и удалять их перед новой загрузкой
// Пример:
// МассивИменДляУдаления = Новый Массив;
// Для Каждого РеквизитФормы Из Реквизиты Цикл
// Если СтрНачинается(РеквизитФормы.Имя, "Колонка_") Тогда
// МассивИменДляУдаления.Добавить(РеквизитФормы.Имя);
// КонецЕсли;
// КонецЦикла;
//
// Для Каждого ИмяУдаляемогоРеквизита Из МассивИменДляУдаления Цикл
// Элементы.Удалить(Элементы[ИмяУдаляемогоРеквизита]);
// УдалитьРеквизит(ИмяУдаляемогоРеквизита);
// КонецЦикла;
КонецПроцедуры
После того как файлы помещены во временное хранилище, мы получаем массив объектов типа ПомещенныйФайлНаСервере, каждый из которых содержит адрес файла во временном хранилище и его имя. Теперь нам нужно динамически создать реквизиты формы, которые будут хранить эти адреса, и соответствующие элементы формы типа "Поле картинки" для их отображения.
В соответствии с рекомендациями, для каждого изображения мы создадим реквизит формы с типом "Строка", а затем на форме добавим элемент "Поле формы" с видом "Поле картинки". Свойство ПутьКДанным этого элемента мы свяжем с созданным строковым реквизитом. Платформа 1С самостоятельно загрузит и отобразит изображение по указанному адресу из временного хранилища.
Рассмотрим пример кода для этой процедуры:
&НаСервере
Процедура СозданиеРеквизитовИЭлементовФормы(МассивФайловДляЗагрузки)
// Соответствие для временного хранения связи между именем реквизита и адресом во временном хранилище
СоответствияРеквизитов = Новый Соответствие;
// Массив для хранения новых реквизитов, которые будут добавлены на форму
НовыеРеквизиты = Новый Массив;
// Перебираем каждый помещенный файл
Для каждого ПомещенныйФайл Из МассивФайловДляЗагрузки Цикл
// Определяем тип реквизита как "Строка"
ТипРеквизита = Новый ОписаниеТипов("Строка");
// Генерируем уникальное имя для реквизита и элемента формы.
// Использование УникальногоИдентификатора помогает избежать проблем,
// если имена файлов содержат недопустимые символы или дублируются.
ИмяКолонки = СтрШаблон("Картинка_%1", СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", ""));
// Создаем новый реквизит формы.
// Параметры: Имя, Тип, Группа (необязательно), Заголовок
РеквизитСтрока = Новый РеквизитФормы(ИмяКолонки, ТипРеквизита, , ПомещенныйФайл.ИмяФайла);
// Добавляем реквизит в массив
НовыеРеквизиты.Добавить(РеквизитСтрока);
// Сохраняем адрес временного хранилища, связанный с именем реквизита
СоответствияРеквизитов[РеквизитСтрока.Имя] = ПомещенныйФайл.Адрес;
КонецЦикла;
// Изменяем реквизиты формы, добавляя новые
ИзменитьРеквизиты(НовыеРеквизиты);
// Теперь создаем элементы формы для отображения картинок
Для Каждого НовыйРеквизит Из НовыеРеквизиты Цикл
// Добавляем новый элемент на форму
НовыйЭлемент = Элементы.Добавить(НовыйРеквизит.Имя, Тип("ПолеФормы"));
// Устанавливаем вид элемента как "Поле картинки"
НовыйЭлемент.Вид = ВидПоляФормы.ПолеКартинки;
// Связываем элемент с созданным реквизитом формы по пути к данным
НовыйЭлемент.ПутьКДанным = НовыйРеквизит.Имя;
// Устанавливаем заголовок элемента
НовыйЭлемент.Заголовок = НовыйРеквизит.Заголовок;
// Важно: если нужно разместить элементы в определенной группе или панели,
// можно указать Родитель:
// НовыйЭлемент.Родитель = Элементы.ВашаГруппаЭлементов; // Например, создайте на форме группу
// Присваиваем созданному реквизиту формы адрес временного хранилища.
// Обращение к реквизиту формы происходит через ЭтотОбъект[ИмяРеквизита]
ЭтотОбъект[НовыйРеквизит.Имя] = СоответствияРеквизитов[НовыйРеквизит.Имя];
КонецЦикла;
КонецПроцедуры
Данный код выполняет следующие действия:
Инициализирует массивы для новых реквизитов и соответствие для связывания имен реквизитов с адресами временного хранилища.
В цикле для каждого помещенного файла:
Определяет тип реквизита как Строка.
Генерирует уникальное имя для реквизита (например, Картинка_УникальныйИдентификатор), чтобы избежать конфликтов и проблем с невалидными символами в именах файлов. Заголовок реквизита при этом может быть равен исходному имени файла.
Создает объект РеквизитФормы и добавляет его в массив НовыеРеквизиты.
Сохраняет адрес временного хранилища в соответствие, привязывая его к уникальному имени реквизита.
Вызывает метод ИзменитьРеквизиты(НовыеРеквизиты) для добавления всех новых реквизитов на форму. Это критически важный шаг.
Во втором цикле для каждого нового реквизита:
Создает новый элемент формы с помощью Элементы.Добавить().
Устанавливает его вид как ВидПоляФормы.ПолеКартинки.
Привязывает элемент к созданному реквизиту через свойство ПутьКДанным.
Присваивает значение (адрес временного хранилища) созданному реквизиту формы, используя синтаксис ЭтотОбъект[НовыйРеквизит.Имя] = АдресХранилища.
После выполнения этой процедуры, на форме будут динамически созданы поля картинок, в которых отобразятся загруженные изображения.
В качестве альтернативного способа отображения изображений можно рассмотреть использование элемента формы "Поле HTML документа". Этот подход позволяет вставлять HTML-код непосредственно в элемент формы, что дает большую гибкость в форматировании и размещении контента.
Мы можем вставлять изображения в HTML-код одним из следующих способов:
Через data:image:: Двоичные данные картинки кодируются в Base64 и вставляются непосредственно в тег <img>. Например: <img src="data:image/png;base64,iVBORw0KGgoAAA...">. Однако этот метод может иметь ограничения по размеру изображений и поддержке на различных платформах (например, в веб-клиенте или на Linux).
Через локальные URL-адреса: Если файлы изображений доступны по локальным URL (например, из временного хранилища или если они были сохранены во временную папку клиента), их можно указать в атрибуте src тега <img>. Однако, прямое использование адреса временного хранилища 1С в Поле HTML документа может быть не всегда корректно интерпретировано встроенным HTML-движком.
Для использования этого метода вам потребуется сгенерировать HTML-код, содержащий теги <img> для каждого изображения (например, при добавлении водяных знаков на pdf), и затем присвоить его свойству Значение элемента "Поле HTML документа".
// Пример на сервере (фрагмент)
&НаСервере
Процедура ОтобразитьКартинкиВHTMLПоле(МассивФайловДляЗагрузки)
// Предполагаем, что у вас на форме есть элемент с именем "ПолеHTML"
// и реквизит формы "ТекстHTML" типа "Строка"
ТекстHTML = Новый ГенераторHTML; // Представим, что есть такой класс/функция
ТекстHTML.ДобавитьНачалоДокумента();
Для Каждого ПомещенныйФайл Из МассивФайловДляЗагрузки Цикл
// Здесь потребуется преобразование двоичных данных в Base64
// или получение пути, доступного для HTML-движка
// Для простоты примера, будем использовать placeholder
АдресКартинки = ПомещенныйФайл.Адрес; // Это адрес во временном хранилище 1С
// В реальной ситуации, если Поле HTML документа не может напрямую открыть адрес 1С:
// 1. Либо сохранить файл временно на клиенте и использовать локальный путь
// 2. Либо получить двоичные данные из хранилища и кодировать в Base64
// ДвоичныеДанные = ПолучитьДвоичныеДанныеИзВременногоХранилища(АдресКартинки);
// Base64Строка = Base64Кодировать(ДвоичныеДанные);
// ТекстHTML.ДобавитьТегImg(СтрШаблон("data:image/png;base64,%1", Base64Строка));
// Если Поле HTML документа поддерживает прямой адрес временного хранилища 1С,
// то это было бы проще, но на практике часто возникают сложности.
ТекстHTML.ДобавитьТегImg(АдресКартинки, "Ширина=200 Высота=150"); // Гипотетический метод
КонецЦикла;
ТекстHTML.ДобавитьКонецДокумента();
// Присваиваем сгенерированный HTML реквизиту формы, который связан с элементом Поле HTML документа
ЭтотОбъект.ТекстHTML = ТекстHTML.ПолучитьСтрокуHTML();
КонецПроцедуры
Несмотря на кажущуюся гибкость, использование "Поля HTML документа" для массового отображения картинок может быть более сложным в реализации и иметь ограничения в производительности и совместимости по сравнению со стандартным "Полем картинки". Прямое использование адресов временного хранилища 1С в HTML часто не поддерживается без дополнительных преобразований.
Массовая загрузка и отображение большого количества изображений на одной форме может привести к значительному потреблению оперативной памяти и ресурсов сервера и клиента, что негативно скажется на производительности и стабильности работы системы. Рассмотрим основные моменты:
Потребление памяти: Каждое изображение, особенно при конвертации в двоичные данные и помещении во временное хранилище, может занимать в памяти до двух раз больше своего реального размера. При загрузке десятков или сотен изображений это может привести к переполнению оперативной памяти.
Загрузка данных: Загрузка большого объема данных по сети (от сервера к клиенту) также занимает время и потребляет ресурсы.
Учитывая потенциальные проблемы с производительностью, мы настоятельно рекомендуем рассмотреть следующие подходы:
Ограничение количества отображаемых изображений: Вместо того чтобы выводить все изображения на форму сразу, отображайте их порциями или в виде миниатюр. Полное изображение можно открывать по требованию, например, по двойному щелчку мыши.
"Виртуализация" списка или "бесконечная прокрутка": Для работы с очень большим количеством изображений (сотни или тысячи) используйте подход, при котором в память загружаются только те изображения, которые видны пользователю в данный момент. Остальные подгружаются по мере прокрутки. Это значительно снижает потребление памяти.
Хранение ссылок: Вместо того чтобы держать все данные изображений в памяти, лучше хранить только пути к файлам или адреса во временном хранилище, а сами изображения загружать и отображать только по мере необходимости.
Использование буферизации: Для более плавной прокрутки или переключения между изображениями можно заранее загружать несколько следующих и предыдущих изображений в буфер.
Кэширование: Оптимизация кэширования данных может снизить нагрузку на память и улучшить отклик системы при повторном обращении к тем же изображениям.
Помните, что отображение сводки файлов в виде таблицы (Имя, Размер файла, Размер изображения, Формат) с возможностью просмотра изображения по щелчку часто является более эффективным и удобным для пользователя подходом, чем вывод всех изображений одновременно.
Если после реализации описанного решения картинки не отображаются, рассмотрите следующие моменты:
Контекст выполнения: Убедитесь, что все операции, связанные с файловой системой (чтение файлов, помещение во временное хранилище) и динамическим изменением реквизитов/элементов формы, выполняются в серверном контексте (&НаСервере).
Правильное связывание реквизитов и элементов: Проверьте, что свойство ПутьКДанным элемента "Поле картинки" точно указывает на имя реквизита формы типа "Строка", который содержит адрес временного хранилища.
Формат изображений: Хотя 1С поддерживает основные форматы (PNG, JPG, JPEG), включая результаты программной конвертации отчетов, убедитесь, что файлы не повреждены и их формат корректно распознается.
Очистка временного хранилища: Адреса во временном хранилище могут быть очищены при закрытии формы или по истечении таймаута, если это указано при помещении файла. Убедитесь, что изображения остаются доступными на протяжении всего жизненного цикла формы.
Повторная загрузка: При повторной загрузке изображений убедитесь, что вы корректно очищаете старые реквизиты и элементы формы перед созданием новых, используя редактор форм в режиме предприятия для отладки структуры.
Тщательный анализ этих аспектов поможет вам успешно реализовать задачу по динамическому отображению изображений во внешней обработке 1С:Предприятие.