ХранилищеЗначения в 1С 8.3: запись, чтение и временное хранилище на управляемых формах

· 8 мин

Содержание

Чтобы сохранить значение в хранилище значений 1С, создайте объект конструктором Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9)) и присвойте его реквизиту с типом ХранилищеЗначения, после чего запишите объект. Достаётся значение обратно методом Получить(). Всё это выполняется только на сервере: на тонком клиенте тип недоступен.

Типичная ситуация: в справочник «Номенклатура» добавили реквизит для фотографии товара или для настроек обработки, а на форме он не отображается и при записи ничего не сохраняется. Причина почти всегда одна — данные пытаются передать через клиент напрямую, минуя временное хранилище формы. Ниже — рабочие схемы для обоих случаев.

Что хранит объект ХранилищеЗначения и где его применяют

В конфигураторе тип называется ХранилищеЗначения (в английском варианте — ValueStorage). В разговорной речи его зовут «хранилищем значений», но в коде пишется именно так, с окончанием «-я».

Это контейнер, который упаковывает любое сериализуемое значение в двоичный вид и кладёт его в одно поле базы данных. Внутри может лежать что угодно из списка поддерживаемых типов:

  • 🖼️ картинка (Картинка) или файл (ДвоичныеДанные);
  • 📊 ТаблицаЗначений, ДеревоЗначений, СписокЗначений;
  • 🧩 Структура, Соответствие, Массив;
  • 📄 ТабличныйДокумент — например, сохранённая печатная форма;
  • 🔗 примитивные типы и ссылки на объекты базы.

Реквизит такого типа можно добавить справочнику, документу, плану видов характеристик, а также регистру сведений (как ресурс или реквизит). Измерением регистра он быть не может.

Где хранитьКогда подходитМинусы
Реквизит объектаМаленькие данные, нужны всегда вместе с объектомЗагружаются при каждом чтении объекта
Ресурс регистра сведений с измерением-ссылкойФайлы, картинки, крупные настройкиНужна отдельная запись и чтение
Хранилище общих настроекНастройки пользователя (ширина колонок, последние параметры)Не для учётных данных
Временное хранилищеПередача между клиентом и сервером, фоновые заданияДанные живут недолго

Программная запись данных в хранилище значений

Базовая схема одна для всех объектов: получить объект, присвоить реквизиту новый ХранилищеЗначения, записать.

&НаСервере
Процедура СохранитьНастройкиОбработки(СсылкаНаЭлемент, ТаблицаНастроек)

    ЭлементОбъект = СсылкаНаЭлемент.ПолучитьОбъект();
    ЭлементОбъект.НастройкиОбработки = Новый ХранилищеЗначения(ТаблицаНастроек, Новый СжатиеДанных(9));
    ЭлементОбъект.Записать();

КонецПроцедуры

Второй параметр конструктора — сжатие. СжатиеДанных принимает уровень от 0 до 9: 0 — без сжатия, 9 — максимальное. Если параметр не указать, данные запишутся без сжатия.

Таблицы значений, тексты и XML сжимаются в несколько раз. Картинки JPEG и PNG, архивы ZIP и PDF уже сжаты — для них уровень 9 почти ничего не даёт, а время записи растёт.

Запись файла с диска сервера

Если файл лежит на диске, доступном серверу 1С, его читают в ДвоичныеДанные и упаковывают:

&НаСервере
Процедура ЗаписатьФайлВТовар(СсылкаНаТовар, ПолноеИмяФайла)

    Данные = Новый ДвоичныеДанные(ПолноеИмяФайла);
    ТоварОбъект = СсылкаНаТовар.ПолучитьОбъект();
    ТоварОбъект.Изображение = Новый ХранилищеЗначения(Данные);
    ТоварОбъект.Записать();

КонецПроцедуры

Путь в ПолноеИмяФайла — это путь на компьютере, где работает сервер 1С, а не на машине пользователя. В клиент-серверной базе файл C:\Фото\товар.jpg будет искаться на сервере. Файлы пользователя передают через временное хранилище — об этом в следующем разделе.

Запись в регистр сведений

Для крупных данных удобнее отдельный регистр сведений : измерение Объект (ссылка) и ресурс Данные с типом ХранилищеЗначения. Так основной объект остаётся лёгким.

&НаСервере
Процедура СохранитьВРегистр(СсылкаНаОбъект, Значение)

    Запись = РегистрыСведений.ДанныеОбъектов.СоздатьМенеджерЗаписи();
    Запись.Объект = СсылкаНаОбъект;
    Запись.Данные = Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9));
    Запись.Записать();

КонецПроцедуры

Менеджер записи по умолчанию замещает существующую запись с тем же измерением, так что повторный вызов просто обновит данные.

Сохранение через форму: выбор файла пользователем

На управляемой форме реквизит типа ХранилищеЗначения нельзя вывести как обычное поле, и на клиент он не передаётся. Рабочая схема из четырёх частей:

  1. На форме создают реквизит АдресКартинки типа Строка и поле картинки, связанное с ним.
  2. При открытии сервер достаёт данные из хранилища и кладёт во временное хранилище формы.
  3. Пользователь выбирает файл, платформа загружает его во временное хранилище и возвращает адрес.
  4. Перед записью сервер забирает данные по адресу и упаковывает в ХранилищеЗначения.
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)

    ДанныеКартинки = ТекущийОбъект.Изображение.Получить();
    Если ДанныеКартинки <> Неопределено Тогда
        АдресКартинки = ПоместитьВоВременноеХранилище(ДанныеКартинки, УникальныйИдентификатор);
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ВыбратьКартинку(Команда)

    Оповещение = Новый ОписаниеОповещения("ВыбратьКартинкуЗавершение", ЭтотОбъект);
    НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);

КонецПроцедуры

&НаКлиенте
Процедура ВыбратьКартинкуЗавершение(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт

    Если Не Результат Тогда
        Возврат; // пользователь закрыл диалог
    КонецЕсли;

    АдресКартинки = Адрес;
    Модифицированность = Истина;

КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)

    Если ЭтоАдресВременногоХранилища(АдресКартинки) Тогда
        Данные = ПолучитьИзВременногоХранилища(АдресКартинки);
        ТекущийОбъект.Изображение = Новый ХранилищеЗначения(Данные);
    КонецЕсли;

КонецПроцедуры

Параметр Истина в НачатьПомещениеФайла открывает диалог выбора файла. Последний параметр привязывает загруженный файл к форме: адрес будет действителен, пока форма открыта. В новых версиях платформы для загрузки есть и НачатьПомещениеФайлаНаСервер — с ней можно показать прогресс и ограничить выбор фильтром.

Поле картинки на форме умеет показывать изображение прямо по адресу временного хранилища. Ничего дополнительно преобразовывать не нужно.

Что видит пользователь типовой конфигурации

В типовых конфигурациях (Бухгалтерия, ЗУП, УТ) пользователь не работает с хранилищем значений напрямую. Картинки товаров и вложения прикрепляют через команду работы с файлами (значок скрепки) на форме объекта. Внутри файлы хранятся в отдельных справочниках и регистрах — по той же логике, что описана выше. Дописывать код нужно только в собственных доработках и расширениях.

Временное хранилище: передача данных между клиентом и сервером

Временное (его ещё называют оперативным) хранилище — это не поле базы, а область памяти сервера в рамках сеанса. Три метода покрывают всю работу:

МетодЧто делает
ПоместитьВоВременноеХранилище(Данные, Адрес)Кладёт значение, возвращает строку-адрес
ПолучитьИзВременногоХранилища(Адрес)Возвращает значение по адресу
УдалитьИзВременногоХранилища(Адрес)Освобождает память досрочно

Второй параметр ПоместитьВоВременноеХранилище определяет срок жизни данных:

  • 🪟 передан УникальныйИдентификатор формы — значение живёт, пока открыта форма, и удаляется при её закрытии;
  • ⏱️ параметр не указан — значение удаляется вскоре, после завершения текущего вызова сервера;
  • 🔁 передан уже существующий адрес — значение по нему заменяется новым.

Результат фонового задания

Частый приём — вернуть таблицу из фонового задания на форму. Адрес готовят заранее, на форме:

&НаСервере
Функция ЗапуститьРасчёт()

    АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
    Параметры = Новый Массив;
    Параметры.Добавить(АдресРезультата);
    ФоновыеЗадания.Выполнить("МойОбщийМодуль.Рассчитать", Параметры);
    Возврат АдресРезультата;

КонецФункции

В процедуре фонового задания результат кладут по тому же адресу: ПоместитьВоВременноеХранилище(Таблица, АдресРезультата). Форма потом читает его через ПолучитьИзВременногоХранилища.

Временное хранилище не заменяет запись в базу. После закрытия формы или завершения сеанса данные исчезают. Всё, что должно остаться, переносите в ХранилищеЗначения и записывайте объект.

Какие типы можно сохранить и что делать при ошибках

В хранилище попадают только значения, которые платформа умеет сериализовать. Не получится сохранить:

  • ❌ COMОбъект и объекты внешних компонент;
  • ❌ форму, элементы формы, ОписаниеОповещения;
  • ❌ открытые потоки чтения и записи (ЧтениеXML, ЗаписьТекста и подобные);
  • ❌ объекты (СправочникОбъект, ДокументОбъект) — вместо них храните ссылку или выгрузку в Структуру.

При попытке передать неподходящий тип конструктор вызовет исключение. Если сохраняемые данные собираются из разных источников, оберните запись в Попытка ... Исключение и запишите текст ошибки в журнал регистрации:

Попытка
    ЭлементОбъект.НастройкиОбработки = Новый ХранилищеЗначения(Настройки, Новый СжатиеДанных(9));
    ЭлементОбъект.Записать();
Исключение
    ЗаписьЖурналаРегистрации("СохранениеНастроек", УровеньЖурналаРегистрации.Ошибка, , ,
        ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    ВызватьИсключение;
КонецПопытки;
Диагностика: выберите симптом

Чтение из хранилища и восстановление состояния

Метод Получить() возвращает ровно то значение, которое было упаковано: таблицу — таблицей, структуру — структурой. Для пустого хранилища он вернёт Неопределено, поэтому проверка обязательна:

&НаСервере
Функция ПрочитатьИзРегистра(СсылкаНаОбъект)

    Запись = РегистрыСведений.ДанныеОбъектов.СоздатьМенеджерЗаписи();
    Запись.Объект = СсылкаНаОбъект;
    Запись.Прочитать();

    Если Запись.Выбран() Тогда
        Возврат Запись.Данные.Получить();
    КонецЕсли;

    Возврат Неопределено;

КонецФункции

Настройки пользователя: хранилище общих настроек

Если задача — запомнить, какие параметры пользователь выбрал в обработке в прошлый раз, отдельный реквизит не нужен. Есть системное хранилище общих настроек:

// сохранить
ХранилищеОбщихНастроек.Сохранить("МояОбработка", "ПоследниеПараметры", СтруктураПараметров);

// восстановить
Параметры = ХранилищеОбщихНастроек.Загрузить("МояОбработка", "ПоследниеПараметры");
Если Параметры = Неопределено Тогда
    Параметры = Новый Структура("Организация, Период");
КонецЕсли;

Настройки сохраняются для текущего пользователя. В типовых конфигурациях на базе БСП принято вызывать обёртки из общего модуля ОбщегоНазначения (методы с именами ХранилищеОбщихНастроекСохранить и ХранилищеОбщихНастроекЗагрузить) — они дополнительно проверяют права.

Картинка по навигационной ссылке

Чтобы показать картинку из реквизита в списке или HTML-поле без переноса во временное хранилище, можно получить навигационную ссылку на сам реквизит: ПолучитьНавигационнуюСсылку(СсылкаНаТовар, "Изображение"). Платформа отдаст содержимое хранилища по этой ссылке.

Как не раздуть базу: практики работы с хранилищем

Хранилище значений удобно, но у него есть ограничения, которые проявляются через полгода эксплуатации.

  1. Не храните крупное в реквизите объекта. Каждое ПолучитьОбъект() читает все реквизиты, включая хранилище. Справочник с фотографиями по 2 МБ на 10 000 позиций замедлит любую обработку, которая перебирает объекты. Выносите такие данные в регистр сведений.
  2. Не рассчитывайте на отбор. Поле типа ХранилищеЗначения нельзя использовать в условиях запроса, сортировке и индексах. Если по данным нужно искать — храните ключевые поля в обычных реквизитах рядом.
  3. Помните про ссылки внутри. Ссылки, упакованные в хранилище, не видит контроль ссылочной целостности. При удалении помеченных объектов платформа не найдёт их внутри хранилища, и после удаления там окажутся «битые» ссылки.
  4. Чистите временное хранилище. Если в цикле помещаете большие файлы без привязки к форме или с привязкой к долгоживущей форме, вызывайте УдалитьИзВременногоХранилища после использования.
  5. Не перезаписывайте без изменений. В примере формы выше хранилище пересоздаётся при каждой записи. Если файлы тяжёлые, заведите флаг «картинка изменена» и пересобирайте ХранилищеЗначения только по нему.

При обмене через XML значение хранилища выгружается как закодированная в Base64 строка. Большие хранилища заметно увеличивают размер пакета обмена — ещё один довод держать их в отдельном регистре и не включать в план обмена без необходимости.

Частые вопросы

Можно ли посмотреть содержимое хранилища в конфигураторе или консоли запросов?
Запрос вернёт объект ХранилищеЗначения, а не содержимое. Чтобы увидеть данные, вызовите Получить() в коде и выведите результат, например таблицу значений, в табличный документ.
Есть ли ограничение на размер данных в хранилище?
Жёсткий лимит определяется СУБД и памятью сервера. На практике файлы больше нескольких десятков мегабайт лучше хранить в томах на диске, а в базе — только путь и метаданные.
Можно ли добавить реквизит типа ХранилищеЗначения в типовую конфигурацию без снятия с поддержки?
Да, через расширение конфигурации: в расширении к заимствованному справочнику добавляют свой реквизит. Основная конфигурация остаётся на поддержке.
Чем ХранилищеЗначения отличается от ДвоичныеДанные?
ДвоичныеДанные — это сам набор байтов, его можно сохранить в файл или передать на клиент. ХранилищеЗначения — упаковка для записи в поле базы, и внутри может быть не только файл, но и таблица, структура или картинка.