Разработка в среде управляемого приложения «1С:Предприятие 8» требует от программиста четкого понимания клиент-серверной архитектуры. Одной из наиболее частых практических задач является передача файлов, в частности изображений номенклатуры, с сервера на сторону клиента для их последующего отображения или сохранения на жестком диске. Начинающие разработчики часто сталкиваются с ситуацией, когда код вроде бы написан правильно, но файл на клиенте оказывается пустым, а методы получения данных возвращают Неопределено. Разберем по шагам, почему возникает эта проблема, как устроено современное хранение файлов в конфигурациях 1С и как правильно реализовать передачу картинок.
Рассмотрим классический пример кода, с которого часто начинают программисты. Разработчик пытается напрямую обратиться к реквизиту справочника присоединенных файлов, извлечь из него хранилище значения и передать его через временное хранилище на клиент:
&НаСервере
Функция ПолучитьКартинкуССервера(СсылкаНаНоменклатуру)
Если ЗначениеЗаполнено(СсылкаНаНоменклатуру.ФайлКартинки) Тогда
Возврат ПоместитьВоВременноеХранилище(СсылкаНаНоменклатуру.ФайлКартинки.ФайлХранилище.Получить());
Иначе
Возврат Ложь;
КонецЕсли;
КонецФункции
При выполнении этого кода метод Получить() возвращает Неопределено, хотя в пользовательском интерфейсе картинка отображается корректно. Проанализируем эту ситуацию.
В современных конфигурациях 1С, созданных на базе БСП (Библиотеки стандартных подсистем), архитектура хранения файлов была существенно оптимизирована для повышения производительности СУБД. Сами справочники присоединенных файлов (например, Справочник.НоменклатураПрисоединенныеФайлы) теперь хранят исключительно метаданные файла: имя, расширение, размер, автора и дату создания. Непосредственно двоичные данные (само тело картинки) физически перенесены в другие места хранения:
По этой причине прямое обращение к реквизиту ФайлХранилище возвращает пустое значение. Платформа просто не хранит там данные в явном виде для предотвращения раздувания таблиц справочников.
Для того чтобы не писать сложные запросы к регистру сведений ДвоичныеДанныеФайлов и не закладывать ручную логику работы с внешними томами, в БСП предусмотрен готовый программный интерфейс в общем модуле РаботаСФайлами. Проанализируем два основных метода, которые помогают решить задачу в одну строку кода.
Если нам необходимо получить двоичные данные файла на сервере, мы используем функцию ДвоичныеДанныеФайла:
ДвоичныеДанные = РаботаСФайлами.ДвоичныеДанныеФайла(СсылкаНаПрисоединенныйФайл);
Если же наша конечная цель — передать этот файл на клиент, БСП предлагает еще более специализированный метод, который автоматически извлекает файл из места его хранения (БД или том на диске) и сразу же помещает во временное хранилище платформы:
АдресХранилища = РаботаСФайлами.ДвоичныеДанныеФайлаВоВременномХранилище(СсылкаНаПрисоединенныйФайл, УникальныйИдентификаторФормы);
Этот подход гарантирует стабильную работу механизма вне зависимости от того, как именно настроено хранение файлов в вашей системе — локально или на сетевых томах.
При передаче файлов между сервером и клиентом ключевую роль играет понимание жизненного цикла Временного хранилища. Рассмотрим распространенную ошибку, связанную со временем жизни адреса временного хранилища.
Если мы помещаем данные в хранилище без указания связи с конкретной формой, платформа уничтожит эти данные сразу же после завершения серверного вызова:
// ОШИБКА: Данные будут удалены при возврате на клиент!
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанные);
Для того чтобы данные оставались доступными на клиенте после завершения работы серверной процедуры, необходимо связать адрес во временном хранилище с уникальным идентификатором управляемой формы. Разберем правильный пример реализации серверной функции:
&НаСервере
Функция ПолучитьАдресКартинкиНаСервере(СсылкаНаНоменклатуру)
СсылкаНаФайл = СсылкаНаНоменклатуру.ФайлКартинки;
Если Не ЗначениеЗаполнено(СсылкаНаФайл) Тогда
Возврат "";
КонецЕсли;
// Передаем УникальныйИдентификатор формы в качестве второго параметра
АдресХранилища = РаботаСФайлами.ДвоичныеДанныеФайлаВоВременномХранилище(СсылкаНаФайл, ЭтаФорма.УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
Теперь адрес во временном хранилище будет валиден до тех пор, пока открыта форма, из которой был произведен вызов.
Довольно часто разработчики совершают избыточные действия: получают картинку на клиенте, записывают ее во временный файл на локальный диск C:\Temp\..., а затем пытаются считать этот файл обратно в элемент управления формы. В управляемом приложении такой подход является грубой ошибкой, так как он сильно снижает производительность, требует настройки прав доступа к файловой системе и не работает в веб-клиенте.
Платформа «1С:Предприятие 8» умеет самостоятельно считывать и визуализировать изображения напрямую по адресу из временного хранилища. Разберем по шагам, как это реализовать:
АдресКартинки, тип данных должен быть Строка.Посмотрим на пример клиент-серверного взаимодействия:
&НаКлиенте
Процедура ПоказатьКартинкуНоменклатуры()
// Получаем адрес картинки во временном хранилище
АдресКартинки = ПолучитьАдресКартинкиНаСервере(Объект.Ссылка);
// Если адрес успешно получен, платформа автоматически обновит поле картинки на форме
Если ПустаяСтрока(АдресКартинки) Тогда
ПоказатьПредупреждение(, "У данной номенклатуры отсутствует изображение!");
КонецЕсли;
КонецПроцедуры
Этот метод работает мгновенно, не засоряет жесткий диск пользователя временными файлами и полностью поддерживается при работе через веб-браузер.
Если перед вами стоит задача именно сохранить картинку на жесткий диск локального компьютера (например, для экспорта или редактирования во внешнем редакторе), необходимо использовать работу с двоичными данными. В современных версиях платформы 1С (начиная с 8.3.15) синхронные методы работы с файлами считаются устаревшими и могут блокироваться в веб-клиенте. Рассмотрим правильный асинхронный способ сохранения файла.
Вместо устаревшего метода Записать() объекта ДвоичныеДанные, мы воспользуемся асинхронной процедурой НачатьПолучениеФайлаССервера. Это позволит скачать файл в фоновом режиме, не замораживая интерфейс программы для пользователя:
&НаКлиенте
Процедура СохранитьКартинкуНаДиск()
АдресКартинки = ПолучитьАдресКартинкиНаСервере(Объект.Ссылка);
Если ПустаяСтрока(АдресКартинки) Тогда
Возврат;
КонецЕсли;
// Определяем имя, под которым файл будет сохранен на клиенте
ИмяФайла = "Изображение_Номенклатуры.jpg";
// Запускаем асинхронный процесс получения файла
ПараметрыПолучения = Новый ПараметрыПолученияФайлаССервера;
НачатьПолучениеФайлаССервера(АдресКартинки, ИмяФайла, ПараметрыПолучения);
КонецПроцедуры
Использование этого метода гарантирует кроссплатформенность вашего решения. Оно будет одинаково надежно работать как в толстом клиенте, так и в тонком клиенте под управлением операционных систем Windows/Linux, а также в любых популярных браузерах.