Хранение тяжелых файлов и картинок непосредственно в базе данных 1С (в реквизитах с типом ХранилищеЗначения) со временем неизбежно приводит к раздуванию базы. Это замедляет создание резервных копий, усложняет обслуживание СУБД и снижает общую производительность системы. Разумным архитектурным решением является вынос медиафайлов на жесткий диск или сетевое хранилище, оставляя в самой базе лишь ссылки или имена файлов.
В этой статье мы подробно разберем, как правильно реализовать загрузку, хранение и отображение картинок из внешней папки в среде 1С:Предприятие 8.3. Мы изучим особенности клиент-серверного взаимодействия, настроим асинхронный импорт файлов и научимся корректно выводить графические данные на управляемую форму.
Прежде чем писать код, проанализируем ключевую проблему новичков. Часто на начальном этапе разработки пишется простой код загрузки, обращающийся к локальному диску клиента (например, C:\MyFolder\image.jpg). В файловой базе на одном компьютере такая схема работает, но при переходе на клиент-серверный вариант (SQL) или при работе нескольких пользователей система выдаст ошибку.
Выясним причину: у каждого пользователя свой уникальный компьютер со своей локальной файловой системой. Сервер 1С, на котором выполняется большая часть бизнес-логики, физически не имеет доступа к папке C:\Users\...\Desktop на компьютере конкретного бухгалтера или менеджера. Из этого следуют важные правила проектирования:
\\ServerName\SharedImages\.ИдентификаторНоменклатуры + ".jpg").Даже идеально написанный код не будет работать, если у сервера 1С нет физических прав на работу с файловой системой. Разберем этот нюанс.
Когда код выполняется на стороне сервера (&НаСервере), операционная система запускает его от имени того пользователя, под которым работает служба сервера 1С:Предприятие. По умолчанию в Windows это локальный пользователь USR1CV8.
Для успешной работы необходимо предоставить пользователю USR1CV8 (или доменной учетной записи, под которой стартует служба сервера 1С) полные права (Чтение, Запись, Изменение) на сетевую папку, выделенную под хранение картинок.
Современные версии платформы 1С 8.3 (особенно начиная с интерфейса «Такси» и версии платформы 8.3.15) требуют асинхронной работы с файловой системой для сохранения адаптивности веб-клиента и мобильных решений. Синхронные методы работы заблокируют интерфейс и вызовут ошибку.
Рассмотрим правильный асинхронный алгоритм помещения файла на сервер с последующей записью в сетевой каталог.
Сначала подготовим процедуру на клиенте, которая открывает диалог выбора файла и передает выбранную картинку во временное хранилище на сервере:
&НаКлиенте
Асинхронная Процедура ЗагрузитьКартинку(Команда)
ПараметрыДиалога = Новый ПараметрыДиалогаВыбораФайла;
ПараметрыДиалога.Фильтр = "Изображения (*.jpg;*.png)|*.jpg;*.png";
ПараметрыДиалога.МножественныйВыбор = Ложь;
ПараметрыДиалога.Заголовок = "Выберите изображение для загрузки";
// Используем асинхронный метод для выбора и помещения файла
РезультатВыбора = Ждать ПоместитьФайлНаСерверАсинх(,,, ПараметрыДиалога, УникальныйИдентификатор);
Если РезультатВыбора <> Неопределено Тогда
// Файл успешно помещен во временное хранилище на сервере.
// Передаем адрес временного хранилища и оригинальное имя файла серверному методу.
СохранитьИзображениеНаСервере(РезультатВыбора.Адрес, РезультатВыбора.Файл.Имя);
// Оповещаем пользователя об успешном завершении операции
ПоказатьПредупреждение(, "Изображение успешно загружено на сервер!");
КонецЕсли;
КонецПроцедуры
Теперь рассмотрим серверный обработчик. На сервере мы извлекаем двоичные данные файла из временного хранилища, создаем уникальное имя файла на основе ссылки объекта и сохраняем файл физически на диск:
&НаСервере
Процедура СохранитьИзображениеНаСервере(АдресВременногоХранилища, ОригинальноеИмяФайла)
// Получаем двоичные данные из временного хранилища по адресу
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресВременногоХранилища);
// Получаем базовый путь из настроек системы (константы или справочника)
БазовыйКаталог = Константы.ПутьКХранилищуФайлов.Получить(); // Например, "\\Server-FS\Images\"
// Гарантируем, что путь заканчивается косой чертой
Если Прав(БазовыйКаталог, 1) <> "\" Тогда
БазовыйКаталог = БазовыйКаталог + "\";
КонецЕсли;
// Проверим, существует ли каталог на диске. Если нет — создадим его.
КаталогНаДиске = Новый Файл(БазовыйКаталог);
Если Не КаталогНаДиске.Существует() Тогда
СоздатьКаталог(БазовыйКаталог);
КонецЕсли;
// Определяем расширение оригинального файла
ТочкаВРеквизите = Найти(ОригинальноеИмяФайла, ".");
Расширение = Сред(ОригинальноеИмяФайла, ТочкаВРеквизите);
// Генерируем уникальное имя файла для предотвращения перезаписи существующих файлов
НовоеИмяФайла = Строка(Объект.Ссылка.УникальныйИдентификатор()) + Расширение;
ПолныйПутьЗаписи = БазовыйКаталог + НовоеИмяФайла;
// Физически записываем двоичные данные файла на диск сервера
ДвоичныеДанныеФайла.Записать(ПолныйПутьЗаписи);
// Сохраняем имя файла в реквизите нашего справочника/документа
Объект.ИмяФайлаКартинки = НовоеИмяФайла;
ЭтотОбъект.Записать();
// Сразу обновляем отображение картинки на форме
АдресКартинкиНаФорме = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, УникальныйИдентификатор);
КонецПроцедуры
На управляемой форме нельзя напрямую сослаться на файл на диске. Отображение картинок строится через специальный строковый реквизит формы, в который помещается навигационная ссылка на двоичные данные во временном хранилище платформы.
Разберем по шагам схему вывода изображения:
АдресКартинкиНаФорме и типом данных Строка.Проанализируем пример кода считывания файла при создании формы на сервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ИнициализироватьОтображениеКартинки();
КонецПроцедуры
&НаСервере
Процедура ИнициализироватьОтображениеКартинки()
// Если имя файла не заполнено, картинки нет
Если Не ЗначениеЗаполнено(Объект.ИмяФайлаКартинки) Тогда
АдресКартинкиНаФорме = "";
Возврат;
КонецЕсли;
БазовыйКаталог = Константы.ПутьКХранилищуФайлов.Получить();
Если Прав(БазовыйКаталог, 1) <> "\" Тогда
БазовыйКаталог = БазовыйКаталог + "\";
КонецЕсли;
ПолныйПутьКФайлу = БазовыйКаталог + Объект.ИмяФайлаКартинки;
// Обязательно проверяем физическое наличие файла на диске перед чтением
ПроверяемыйФайл = Новый Файл(ПолныйПутьКФайлу);
Если ПроверяемыйФайл.Существует() Тогда
// Считываем файл в двоичные данные
ДвоичныеДанныеИзображения = Новый ДвоичныеДанные(ПолныйПутьКФайлу);
// Помещаем во временное хранилище и привязываем к уникальному идентификатору формы
АдресКартинкиНаФорме = ПоместитьВоВременноеХранилище(ДвоичныеДанныеИзображения, УникальныйИдентификатор);
Иначе
// Если файл физически удален с диска, очищаем реквизит или выводим заглушку
АдресКартинкиНаФорме = "";
КонецЕсли;
КонецПроцедуры
Если вы работаете в старых конфигурациях с обычными формами, то механизм временных хранилищ использовать не нужно. Там вся работа происходит в одном контексте, и вывод картинки на форму реализуется гораздо проще (синхронно).
Посмотрим на пример кода отображения картинки в обычном приложении при открытии формы:
Процедура ПриОткрытии()
БазовыйПуть = "C:\Users\Server1C\Desktop\Картинки\"; // Или сетевой путь
ПолныйПутьКФайлу = БазовыйПуть + СокрЛП(Наименование) + ".jpg";
ФайлКартинки = Новый Файл(ПолныйПутьКФайлу);
Если ФайлКартинки.Существует() Тогда
// Напрямую инициализируем объект Картинка по пути файла
ЭлементыФормы.ОсновноеИзображение.Картинка = Новый Картинка(ПолныйПутьКФайлу);
Иначе
ЭлементыФормы.ОсновноеИзображение.Картинка = Новый Картинка; // Очищаем поле
КонецЕсли;
КонецПроцедуры
Если вы разрабатываете решение на базе типовой конфигурации (например, УТ, ERP, УНФ) или ваша нетиповая конфигурация содержит интегрированную Библиотеку Стандартных Подсистем (БСП), крайне рекомендуется использовать встроенную подсистему «Работа с файлами».
Преимущества хранения файлов в томах при использовании БСП: