ХранилищеЗначения в 1С 8.3: запись, чтение и временное хранилище на управляемых формах
Содержание
Чтобы сохранить значение в хранилище значений 1С, создайте объект конструктором Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9)) и присвойте его реквизиту с типом ХранилищеЗначения, после чего запишите объект. Достаётся значение обратно методом Получить(). Всё это выполняется только на сервере: на тонком клиенте тип недоступен.
Типичная ситуация: в справочник «Номенклатура» добавили реквизит для фотографии товара или для настроек обработки, а на форме он не отображается и при записи ничего не сохраняется. Причина почти всегда одна — данные пытаются передать через клиент напрямую, минуя временное хранилище формы. Ниже — рабочие схемы для обоих случаев.
Что хранит объект ХранилищеЗначения и где его применяют
В конфигураторе тип называется ХранилищеЗначения (в английском варианте — ValueStorage). В разговорной речи его зовут «хранилищем значений», но в коде пишется именно так, с окончанием «-я».
Это контейнер, который упаковывает любое сериализуемое значение в двоичный вид и кладёт его в одно поле базы данных. Внутри может лежать что угодно из списка поддерживаемых типов:
- 🖼️ картинка (
Картинка) или файл (ДвоичныеДанные); - 📊
ТаблицаЗначений,ДеревоЗначений,СписокЗначений; - 🧩
Структура,Соответствие,Массив; - 📄
ТабличныйДокумент— например, сохранённая печатная форма; - 🔗 примитивные типы и ссылки на объекты базы.
Реквизит такого типа можно добавить справочнику, документу, плану видов характеристик, а также регистру сведений (как ресурс или реквизит). Измерением регистра он быть не может.
| Где хранить | Когда подходит | Минусы |
|---|---|---|
| Реквизит объекта | Маленькие данные, нужны всегда вместе с объектом | Загружаются при каждом чтении объекта |
| Ресурс регистра сведений с измерением-ссылкой | Файлы, картинки, крупные настройки | Нужна отдельная запись и чтение |
| Хранилище общих настроек | Настройки пользователя (ширина колонок, последние параметры) | Не для учётных данных |
| Временное хранилище | Передача между клиентом и сервером, фоновые задания | Данные живут недолго |
Программная запись данных в хранилище значений
Базовая схема одна для всех объектов: получить объект, присвоить реквизиту новый ХранилищеЗначения, записать.
&НаСервере
Процедура СохранитьНастройкиОбработки(СсылкаНаЭлемент, ТаблицаНастроек)
ЭлементОбъект = СсылкаНаЭлемент.ПолучитьОбъект();
ЭлементОбъект.НастройкиОбработки = Новый ХранилищеЗначения(ТаблицаНастроек, Новый СжатиеДанных(9));
ЭлементОбъект.Записать();
КонецПроцедуры
Второй параметр конструктора — сжатие. СжатиеДанных принимает уровень от 0 до 9: 0 — без сжатия, 9 — максимальное. Если параметр не указать, данные запишутся без сжатия.
Таблицы значений, тексты и XML сжимаются в несколько раз. Картинки JPEG и PNG, архивы ZIP и PDF уже сжаты — для них уровень 9 почти ничего не даёт, а время записи растёт.
Запись файла с диска сервера
Если файл лежит на диске, доступном серверу 1С, его читают в ДвоичныеДанные и упаковывают:
&НаСервере
Процедура ЗаписатьФайлВТовар(СсылкаНаТовар, ПолноеИмяФайла)
Данные = Новый ДвоичныеДанные(ПолноеИмяФайла);
ТоварОбъект = СсылкаНаТовар.ПолучитьОбъект();
ТоварОбъект.Изображение = Новый ХранилищеЗначения(Данные);
ТоварОбъект.Записать();
КонецПроцедуры
Путь в ПолноеИмяФайла — это путь на компьютере, где работает сервер 1С, а не на машине пользователя. В клиент-серверной базе файл C:\Фото\товар.jpg будет искаться на сервере. Файлы пользователя передают через временное хранилище — об этом в следующем разделе.
Запись в регистр сведений
Для крупных данных удобнее отдельный регистр сведений
: измерение Объект (ссылка) и ресурс Данные с типом ХранилищеЗначения. Так основной объект остаётся лёгким.
&НаСервере
Процедура СохранитьВРегистр(СсылкаНаОбъект, Значение)
Запись = РегистрыСведений.ДанныеОбъектов.СоздатьМенеджерЗаписи();
Запись.Объект = СсылкаНаОбъект;
Запись.Данные = Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9));
Запись.Записать();
КонецПроцедуры
Менеджер записи по умолчанию замещает существующую запись с тем же измерением, так что повторный вызов просто обновит данные.
Сохранение через форму: выбор файла пользователем
На управляемой форме реквизит типа ХранилищеЗначения нельзя вывести как обычное поле, и на клиент он не передаётся. Рабочая схема из четырёх частей:
- На форме создают реквизит
АдресКартинкитипаСтрокаи поле картинки, связанное с ним. - При открытии сервер достаёт данные из хранилища и кладёт во временное хранилище формы.
- Пользователь выбирает файл, платформа загружает его во временное хранилище и возвращает адрес.
- Перед записью сервер забирает данные по адресу и упаковывает в
ХранилищеЗначения.
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
ДанныеКартинки = ТекущийОбъект.Изображение.Получить();
Если ДанныеКартинки <> Неопределено Тогда
АдресКартинки = ПоместитьВоВременноеХранилище(ДанныеКартинки, УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ВыбратьКартинку(Команда)
Оповещение = Новый ОписаниеОповещения("ВыбратьКартинкуЗавершение", ЭтотОбъект);
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ВыбратьКартинкуЗавершение(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если Не Результат Тогда
Возврат; // пользователь закрыл диалог
КонецЕсли;
АдресКартинки = Адрес;
Модифицированность = Истина;
КонецПроцедуры
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЭтоАдресВременногоХранилища(АдресКартинки) Тогда
Данные = ПолучитьИзВременногоХранилища(АдресКартинки);
ТекущийОбъект.Изображение = Новый ХранилищеЗначения(Данные);
КонецЕсли;
КонецПроцедуры
Параметр Истина в НачатьПомещениеФайла открывает диалог выбора файла. Последний параметр привязывает загруженный файл к форме: адрес будет действителен, пока форма открыта. В новых версиях платформы для загрузки есть и НачатьПомещениеФайлаНаСервер — с ней можно показать прогресс и ограничить выбор фильтром.
Поле картинки на форме умеет показывать изображение прямо по адресу временного хранилища. Ничего дополнительно преобразовывать не нужно.
Что видит пользователь типовой конфигурации
В типовых конфигурациях (Бухгалтерия, ЗУП, УТ) пользователь не работает с хранилищем значений напрямую. Картинки товаров и вложения прикрепляют через команду работы с файлами (значок скрепки) на форме объекта. Внутри файлы хранятся в отдельных справочниках и регистрах — по той же логике, что описана выше. Дописывать код нужно только в собственных доработках и расширениях.
Временное хранилище: передача данных между клиентом и сервером
Временное (его ещё называют оперативным) хранилище — это не поле базы, а область памяти сервера в рамках сеанса. Три метода покрывают всю работу:
| Метод | Что делает |
|---|---|
ПоместитьВоВременноеХранилище(Данные, Адрес) | Кладёт значение, возвращает строку-адрес |
ПолучитьИзВременногоХранилища(Адрес) | Возвращает значение по адресу |
УдалитьИзВременногоХранилища(Адрес) | Освобождает память досрочно |
Второй параметр ПоместитьВоВременноеХранилище определяет срок жизни данных:
- 🪟 передан
УникальныйИдентификаторформы — значение живёт, пока открыта форма, и удаляется при её закрытии; - ⏱️ параметр не указан — значение удаляется вскоре, после завершения текущего вызова сервера;
- 🔁 передан уже существующий адрес — значение по нему заменяется новым.
Результат фонового задания
Частый приём — вернуть таблицу из фонового задания на форму. Адрес готовят заранее, на форме:
&НаСервере
Функция ЗапуститьРасчёт()
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
Параметры = Новый Массив;
Параметры.Добавить(АдресРезультата);
ФоновыеЗадания.Выполнить("МойОбщийМодуль.Рассчитать", Параметры);
Возврат АдресРезультата;
КонецФункции
В процедуре фонового задания результат кладут по тому же адресу: ПоместитьВоВременноеХранилище(Таблица, АдресРезультата). Форма потом читает его через ПолучитьИзВременногоХранилища.
Временное хранилище не заменяет запись в базу. После закрытия формы или завершения сеанса данные исчезают. Всё, что должно остаться, переносите в ХранилищеЗначения и записывайте объект.
Какие типы можно сохранить и что делать при ошибках
В хранилище попадают только значения, которые платформа умеет сериализовать. Не получится сохранить:
- ❌
COMОбъекти объекты внешних компонент; - ❌ форму, элементы формы,
ОписаниеОповещения; - ❌ открытые потоки чтения и записи (
ЧтениеXML,ЗаписьТекстаи подобные); - ❌ объекты (
СправочникОбъект,ДокументОбъект) — вместо них храните ссылку или выгрузку вСтруктуру.
При попытке передать неподходящий тип конструктор вызовет исключение. Если сохраняемые данные собираются из разных источников, оберните запись в Попытка ... Исключение и запишите текст ошибки в журнал регистрации:
Попытка
ЭлементОбъект.НастройкиОбработки = Новый ХранилищеЗначения(Настройки, Новый СжатиеДанных(9));
ЭлементОбъект.Записать();
Исключение
ЗаписьЖурналаРегистрации("СохранениеНастроек", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
Чтение из хранилища и восстановление состояния
Метод Получить() возвращает ровно то значение, которое было упаковано: таблицу — таблицей, структуру — структурой. Для пустого хранилища он вернёт Неопределено, поэтому проверка обязательна:
&НаСервере
Функция ПрочитатьИзРегистра(СсылкаНаОбъект)
Запись = РегистрыСведений.ДанныеОбъектов.СоздатьМенеджерЗаписи();
Запись.Объект = СсылкаНаОбъект;
Запись.Прочитать();
Если Запись.Выбран() Тогда
Возврат Запись.Данные.Получить();
КонецЕсли;
Возврат Неопределено;
КонецФункции
Настройки пользователя: хранилище общих настроек
Если задача — запомнить, какие параметры пользователь выбрал в обработке в прошлый раз, отдельный реквизит не нужен. Есть системное хранилище общих настроек:
// сохранить
ХранилищеОбщихНастроек.Сохранить("МояОбработка", "ПоследниеПараметры", СтруктураПараметров);
// восстановить
Параметры = ХранилищеОбщихНастроек.Загрузить("МояОбработка", "ПоследниеПараметры");
Если Параметры = Неопределено Тогда
Параметры = Новый Структура("Организация, Период");
КонецЕсли;
Настройки сохраняются для текущего пользователя. В типовых конфигурациях на базе БСП принято вызывать обёртки из общего модуля ОбщегоНазначения (методы с именами ХранилищеОбщихНастроекСохранить и ХранилищеОбщихНастроекЗагрузить) — они дополнительно проверяют права.
Картинка по навигационной ссылке
Чтобы показать картинку из реквизита в списке или HTML-поле без переноса во временное хранилище, можно получить навигационную ссылку на сам реквизит: ПолучитьНавигационнуюСсылку(СсылкаНаТовар, "Изображение"). Платформа отдаст содержимое хранилища по этой ссылке.
Как не раздуть базу: практики работы с хранилищем
Хранилище значений удобно, но у него есть ограничения, которые проявляются через полгода эксплуатации.
- Не храните крупное в реквизите объекта. Каждое
ПолучитьОбъект()читает все реквизиты, включая хранилище. Справочник с фотографиями по 2 МБ на 10 000 позиций замедлит любую обработку, которая перебирает объекты. Выносите такие данные в регистр сведений. - Не рассчитывайте на отбор. Поле типа
ХранилищеЗначениянельзя использовать в условиях запроса, сортировке и индексах. Если по данным нужно искать — храните ключевые поля в обычных реквизитах рядом. - Помните про ссылки внутри. Ссылки, упакованные в хранилище, не видит контроль ссылочной целостности. При удалении помеченных объектов платформа не найдёт их внутри хранилища, и после удаления там окажутся «битые» ссылки.
- Чистите временное хранилище. Если в цикле помещаете большие файлы без привязки к форме или с привязкой к долгоживущей форме, вызывайте
УдалитьИзВременногоХранилищапосле использования. - Не перезаписывайте без изменений. В примере формы выше хранилище пересоздаётся при каждой записи. Если файлы тяжёлые, заведите флаг «картинка изменена» и пересобирайте
ХранилищеЗначениятолько по нему.
При обмене через XML значение хранилища выгружается как закодированная в Base64 строка. Большие хранилища заметно увеличивают размер пакета обмена — ещё один довод держать их в отдельном регистре и не включать в план обмена без необходимости.