К статье
Практический материал

Разработка и Конфигурация · 10 мин

Хранилище значений в 1С: как записать данные правильно

Как записать данные в хранилище значений 1С: создание объекта ХранилищеЗначения, запись в реквизиты, сжатие, сериализация и типичные ошибки разработчика.

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

Типичная задача выглядит так: в справочнике «Номенклатура» есть реквизит «Картинка» типа ХранилищеЗначения, и нужно сохранить туда загруженный пользователем файл изображения. Для этого файл сначала читается в двоичные данные или картинку, затем оборачивается в хранилище и записывается вместе с объектом.

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

Что такое ХранилищеЗначения и когда его использовать

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

Типичные сценарии применения: хранение изображений товаров и сотрудников, прикреплённых файлов, макетов и табличных документов, настроек в виде структур и соответствий, кэша результатов тяжёлых вычислений. В типовых конфигурациях хранилище используется, например, для фотографий физических лиц и печатных форм.

Хранилище не подходит для данных, по которым нужно искать, сортировать или строить отчёты. Если по значению требуется отбор в запросе — храните его в обычном реквизите примитивного или ссылочного типа.

Как создать реквизит типа ХранилищеЗначения

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

Если данных много и они крупные (файлы, изображения), в типовых конфигурациях принято выносить их в отдельный регистр сведений или справочник присоединённых файлов, а не хранить прямо в основном объекте. Это ускоряет чтение списков: платформа не поднимает тяжёлое поле при каждом обращении к элементу.

После добавления реквизита обновите конфигурацию базы данных (F7). Если реквизит добавляется в существующую таблицу с данными, реструктуризация пройдёт быстро — поле хранилища добавляется без пересчёта данных.

Запись значения в хранилище: базовый код

Минимальный сценарий записи строки или другого простого значения:

Объект = Справочники.Номенклатура.НайтиПоКоду("0000001").ПолучитьОбъект();

Объект.ДополнительныеДанные = Новый ХранилищеЗначения("Текст для сохранения");

Объект.Записать();

Запись файла с диска в реквизит-картинку:

ДвоичныеДанные = Новый ДвоичныеДанные("C:\Images\photo.jpg");

Объект.Картинка = Новый ХранилищеЗначения(ДвоичныеДанные);

Объект.Записать();

Чтение значения обратно выполняется через метод Получить(). Если реквизит пуст, метод вернёт Неопределено — это нужно учитывать, чтобы не получить ошибку при обращении к свойствам результата:

Значение = Объект.ДополнительныеДанные.Получить();

Если Значение = Неопределено Тогда

// реквизит пуст

КонецЕсли;

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

Сжатие данных при записи

Конструктор хранилища принимает второй необязательный параметр — объект СжатиеДанных с уровнем от 0 (без сжатия) до 9 (максимальное). По умолчанию применяется сжатие, и для большинства случаев менять его не нужно.

Явно указывать сжатие имеет смысл в двух ситуациях: когда данные уже сжаты (JPEG, ZIP, видео — повторное сжатие бесполезно и тратит время) и когда требуется максимально уменьшить объём хранимых XML, текстов или табличных документов. Для уже сжатых форматов можно передать Новый СжатиеДанных(0).

Чтение не зависит от уровня сжатия: метод Получить() автоматически распаковывает данные, дополнительных действий не требуется.

Какой уровень сжатия выбрать

По умолчанию

Подходит для текста, XML, структур, табличных документов. Разумный баланс скорости и объёма.

СжатиеДанных(9)

Для больших текстовых данных при дефиците места в базе. Запись медленнее.

СжатиеДанных(0)

Для уже сжатых форматов: JPEG, PNG, ZIP, PDF. Избавляет от бесполезной работы архиватора.

Запись сложных типов: структуры, таблицы, табличные документы

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

Настройки = Новый Структура;

Настройки.Вставить("Организация", ТекущаяОрганизация);

Настройки.Вставить("ПоказыватьАрхив", Истина);

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

МенеджерЗаписи.Пользователь = Пользователь;

МенеджерЗаписи.Настройки = Новый ХранилищеЗначения(Настройки);

МенеджерЗаписи.Записать();

Не все типы сериализуются. Нельзя положить в хранилище объекты, привязанные к контексту сеанса или базы: СправочникОбъект, ДокументОбъект, запрос с открытым результатом, COM-объекты. Попытка приведёт к ошибке о невозможности сериализации. Ссылки (СправочникСсылка, ДокументСсылка) сериализуются нормально.

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

Для регистра сведений запись выполняется через менеджер записи или набор записей. Важно заполнить все измерения — иначе запись либо не создастся, либо перезапишет не ту строку. После Записать() данные сразу доступны для чтения другими сеансами.

Отдельный частный случай — параметры сеанса типа ХранилищеЗначения. Они позволяют один раз вычислить значение (например, настройки текущего пользователя) и использовать его весь сеанс. Присвоение выполняется обычным образом: ПараметрыСеанса.Настройки = Новый ХранилищеЗначения(Настройки), чаще всего в обработчике УстановкаПараметровСеанса.

Для временного хранения данных между вызовами сервера в управляемых формах хранилище значений базы не нужно — используйте ВременноеХранилище через ПоместитьВоВременноеХранилище(). Это разные механизмы: временное хранилище живёт в памяти сервера и не пишется в базу.

Порядок записи файла в хранилище
  1. 1
    Добавьте в объект реквизит типа ХранилищеЗначения и обновите конфигурацию БД
  2. 2
    Прочитайте файл: Новый ДвоичныеДанные(ПутьКФайлу) или загрузите через НачатьПомещениеФайла
  3. 3
    Создайте хранилище: Новый ХранилищеЗначения(ДвоичныеДанные)
  4. 4
    Присвойте хранилище реквизиту объекта
  5. 5
    Вызовите Объект.Записать()
  6. 6
    Проверьте чтение: Результат = Объект.Реквизит.Получить()

Типичные ошибки и как их проверить

Самая частая ошибка — попытка прочитать значение без Получить(): разработчик обращается к реквизиту и ожидает увидеть строку или картинку, а получает объект ХранилищеЗначения. Симптом — ошибки вида «Поле объекта не обнаружено» при обращении к свойствам результата.

Вторая группа проблем — забытый вызов Записать() после присвоения реквизита и работа со ссылкой вместо объекта: у ссылки реквизиты доступны только для чтения, присвоение не сохранится. Всегда используйте ПолучитьОбъект() или создавайте новый объект.

Третья ошибка — попытка изменить существующее хранилище. Объект иммутабелен: нужно получить значение, изменить его, создать новое хранилище и записать поверх. Также помните, что при групповом перезаполнении хранилищ с файлами база может существенно вырасти — контролируйте объём и делайте резервную копию перед массовыми операциями.

Типичные ошибки
01
Чтение реквизита без Получить() — вместо данных получаете объект ХранилищеЗначения и ошибку при обращении к свойствам
02
Присвоение реквизиту через ссылку без ПолучитьОбъект() — изменения не сохраняются
03
Попытка изменить существующее хранилище — объект неизменяем, нужно создавать новый
04
Хранение ссылок внутри сериализованной структуры — после удаления объекта получаете битые ссылки
05
Запись несериализуемых типов (СправочникОбъект, COMОбъект) — ошибка сериализации в момент создания хранилища
Контрольные точки

Как проверить результат записи

Чтение реквизита
Норма

Получить() возвращает исходное значение того же типа

Тревожный признак

Возвращается Неопределено — запись не выполнена или реквизит не тот

Тип результата
Норма

ТипЗнч(Результат) совпадает с типом записанного значения

Тревожный признак

Тип — ХранилищеЗначения, значит забыли вызвать Получить()

Объём базы после массовой записи
Норма

Рост соответствует суммарному размеру файлов с учётом сжатия

Тревожный признак

Резкий неконтролируемый рост — возможно, пишете дубликаты в цикле

Итог

Что важно запомнить

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

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

Можно ли использовать ХранилищеЗначения в запросе и отобрать по его содержимому?

Нет. В запросе можно получить само поле хранилища, но нельзя накладывать условия на его содержимое. Для поиска по значению храните его дублирующее представление (например, хеш или текстовое поле) в отдельном реквизите.

Что вернёт Получить(), если реквизит пустой?

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

Как изменить уже записанное значение в хранилище?

Хранилище неизменяемо. Получите значение методом Получить(), измените его, создайте новый объект Новый ХранилищеЗначения с обновлёнными данными и запишите объект заново.

Можно ли хранить в хранилище таблицу значений или табличный документ?

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

Чем ХранилищеЗначения отличается от ВременногоХранилища?

ХранилищеЗначения — тип данных для постоянного сохранения в полях базы. ВременноеХранилище — механизм серверной памяти для передачи данных между вызовами в течение сеанса, в базу оно не пишется и очищается по адресу или таймауту.

Стоит ли хранить файлы прямо в реквизите справочника?

Для небольших данных (подписи, печати, маленькие фото) — допустимо. Для большого числа файлов лучше отдельный регистр сведений или справочник присоединённых файлов: тогда списки и формы основного объекта не замедляются чтением тяжёлого поля.