Разработка и Конфигурация · 10 мин
Хранилище значений в 1С: как записать данные правильно
Как записать данные в хранилище значений 1С: создание объекта ХранилищеЗначения, запись в реквизиты, сжатие, сериализация и типичные ошибки разработчика.
Хранилище значений (ХранилищеЗначения) — встроенный тип платформы 1С:Предприятие, который позволяет сохранять в полях базы данных практически любые данные: картинки, файлы, табличные документы, структуры, массивы, результаты запросов. Записать значение в хранилище можно только программно — через конструктор Новый ХранилищеЗначения(Значение), а прочитать — методом Получить().
Типичная задача выглядит так: в справочнике «Номенклатура» есть реквизит «Картинка» типа ХранилищеЗначения, и нужно сохранить туда загруженный пользователем файл изображения. Для этого файл сначала читается в двоичные данные или картинку, затем оборачивается в хранилище и записывается вместе с объектом.
Важно понимать ограничения: значение, помещённое в хранилище, не участвует в запросах напрямую — нельзя отобрать записи по содержимому хранилища или вывести его в отчёт без программного извлечения. Также хранилище нельзя редактировать после создания: чтобы изменить данные, нужно создать новый объект ХранилищеЗначения и перезаписать реквизит.
Что такое ХранилищеЗначения и когда его использовать
ХранилищеЗначения — это сериализуемый контейнер: платформа преобразует переданное значение во внутренний формат и сохраняет его в поле базы данных как единый блок. При чтении метод Получить() выполняет обратную десериализацию и возвращает исходное значение.
Типичные сценарии применения: хранение изображений товаров и сотрудников, прикреплённых файлов, макетов и табличных документов, настроек в виде структур и соответствий, кэша результатов тяжёлых вычислений. В типовых конфигурациях хранилище используется, например, для фотографий физических лиц и печатных форм.
Хранилище не подходит для данных, по которым нужно искать, сортировать или строить отчёты. Если по значению требуется отбор в запросе — храните его в обычном реквизите примитивного или ссылочного типа.
Как создать реквизит типа ХранилищеЗначения
В конфигураторе откройте объект (справочник, документ, регистр сведений) и добавьте реквизит. В свойстве «Тип» выберите «ХранилищеЗначения». Для регистра сведений хранилище может быть как ресурсом, так и реквизитом — чаще используют ресурс, когда хранилище привязано к ключу измерений.
Если данных много и они крупные (файлы, изображения), в типовых конфигурациях принято выносить их в отдельный регистр сведений или справочник присоединённых файлов, а не хранить прямо в основном объекте. Это ускоряет чтение списков: платформа не поднимает тяжёлое поле при каждом обращении к элементу.
После добавления реквизита обновите конфигурацию базы данных (F7). Если реквизит добавляется в существующую таблицу с данными, реструктуризация пройдёт быстро — поле хранилища добавляется без пересчёта данных.
Запись значения в хранилище: базовый код
Минимальный сценарий записи строки или другого простого значения:
Объект = Справочники.Номенклатура.НайтиПоКоду("0000001").ПолучитьОбъект();
Объект.ДополнительныеДанные = Новый ХранилищеЗначения("Текст для сохранения");
Объект.Записать();
Запись файла с диска в реквизит-картинку:
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Images\photo.jpg");
Объект.Картинка = Новый ХранилищеЗначения(ДвоичныеДанные);
Объект.Записать();
Чтение значения обратно выполняется через метод Получить(). Если реквизит пуст, метод вернёт Неопределено — это нужно учитывать, чтобы не получить ошибку при обращении к свойствам результата:
Значение = Объект.ДополнительныеДанные.Получить();
Если Значение = Неопределено Тогда
// реквизит пуст
КонецЕсли;
Объект.Картинка = Новый ХранилищеЗначения(Новый ДвоичныеДанные(ПутьКФайлу), Новый СжатиеДанных(9));
Объект.Записать();
СохраненныеДанные = Объект.Картинка.Получить();Сжатие данных при записи
Конструктор хранилища принимает второй необязательный параметр — объект СжатиеДанных с уровнем от 0 (без сжатия) до 9 (максимальное). По умолчанию применяется сжатие, и для большинства случаев менять его не нужно.
Явно указывать сжатие имеет смысл в двух ситуациях: когда данные уже сжаты (JPEG, ZIP, видео — повторное сжатие бесполезно и тратит время) и когда требуется максимально уменьшить объём хранимых XML, текстов или табличных документов. Для уже сжатых форматов можно передать Новый СжатиеДанных(0).
Чтение не зависит от уровня сжатия: метод Получить() автоматически распаковывает данные, дополнительных действий не требуется.
Какой уровень сжатия выбрать
Подходит для текста, XML, структур, табличных документов. Разумный баланс скорости и объёма.
Для больших текстовых данных при дефиците места в базе. Запись медленнее.
Для уже сжатых форматов: JPEG, PNG, ZIP, PDF. Избавляет от бесполезной работы архиватора.
Запись сложных типов: структуры, таблицы, табличные документы
В хранилище можно помещать сериализуемые коллекции: структуры, массивы, соответствия, таблицы значений, табличные документы. Это удобно для сохранения настроек пользователя или снимка данных на момент времени.
Настройки = Новый Структура;
Настройки.Вставить("Организация", ТекущаяОрганизация);
Настройки.Вставить("ПоказыватьАрхив", Истина);
МенеджерЗаписи = РегистрыСведений.НастройкиПользователей.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Пользователь = Пользователь;
МенеджерЗаписи.Настройки = Новый ХранилищеЗначения(Настройки);
МенеджерЗаписи.Записать();
Не все типы сериализуются. Нельзя положить в хранилище объекты, привязанные к контексту сеанса или базы: СправочникОбъект, ДокументОбъект, запрос с открытым результатом, COM-объекты. Попытка приведёт к ошибке о невозможности сериализации. Ссылки (СправочникСсылка, ДокументСсылка) сериализуются нормально.
Запись в регистр сведений и в параметры сеанса
Для регистра сведений запись выполняется через менеджер записи или набор записей. Важно заполнить все измерения — иначе запись либо не создастся, либо перезапишет не ту строку. После Записать() данные сразу доступны для чтения другими сеансами.
Отдельный частный случай — параметры сеанса типа ХранилищеЗначения. Они позволяют один раз вычислить значение (например, настройки текущего пользователя) и использовать его весь сеанс. Присвоение выполняется обычным образом: ПараметрыСеанса.Настройки = Новый ХранилищеЗначения(Настройки), чаще всего в обработчике УстановкаПараметровСеанса.
Для временного хранения данных между вызовами сервера в управляемых формах хранилище значений базы не нужно — используйте ВременноеХранилище через ПоместитьВоВременноеХранилище(). Это разные механизмы: временное хранилище живёт в памяти сервера и не пишется в базу.
- 1Добавьте в объект реквизит типа ХранилищеЗначения и обновите конфигурацию БД
- 2Прочитайте файл: Новый ДвоичныеДанные(ПутьКФайлу) или загрузите через НачатьПомещениеФайла
- 3Создайте хранилище: Новый ХранилищеЗначения(ДвоичныеДанные)
- 4Присвойте хранилище реквизиту объекта
- 5Вызовите Объект.Записать()
- 6Проверьте чтение: Результат = Объект.Реквизит.Получить()
Типичные ошибки и как их проверить
Самая частая ошибка — попытка прочитать значение без Получить(): разработчик обращается к реквизиту и ожидает увидеть строку или картинку, а получает объект ХранилищеЗначения. Симптом — ошибки вида «Поле объекта не обнаружено» при обращении к свойствам результата.
Вторая группа проблем — забытый вызов Записать() после присвоения реквизита и работа со ссылкой вместо объекта: у ссылки реквизиты доступны только для чтения, присвоение не сохранится. Всегда используйте ПолучитьОбъект() или создавайте новый объект.
Третья ошибка — попытка изменить существующее хранилище. Объект иммутабелен: нужно получить значение, изменить его, создать новое хранилище и записать поверх. Также помните, что при групповом перезаполнении хранилищ с файлами база может существенно вырасти — контролируйте объём и делайте резервную копию перед массовыми операциями.
Как проверить результат записи
Получить() возвращает исходное значение того же типа
Возвращается Неопределено — запись не выполнена или реквизит не тот
ТипЗнч(Результат) совпадает с типом записанного значения
Тип — ХранилищеЗначения, значит забыли вызвать Получить()
Рост соответствует суммарному размеру файлов с учётом сжатия
Резкий неконтролируемый рост — возможно, пишете дубликаты в цикле
Что важно запомнить
Запись в хранилище значений — это три шага: создать объект Новый ХранилищеЗначения(Значение), присвоить реквизиту, вызвать Записать(). Чтение — только через Получить(). Хранилище неизменяемо, не участвует в запросах и подходит для файлов, картинок и настроек, но не для данных, по которым нужен отбор.
Частые вопросы
Можно ли использовать ХранилищеЗначения в запросе и отобрать по его содержимому?
Нет. В запросе можно получить само поле хранилища, но нельзя накладывать условия на его содержимое. Для поиска по значению храните его дублирующее представление (например, хеш или текстовое поле) в отдельном реквизите.
Что вернёт Получить(), если реквизит пустой?
Неопределено. Перед использованием результата всегда проверяйте его на Неопределено, иначе обращение к свойству или методу вызовет ошибку выполнения.
Как изменить уже записанное значение в хранилище?
Хранилище неизменяемо. Получите значение методом Получить(), измените его, создайте новый объект Новый ХранилищеЗначения с обновлёнными данными и запишите объект заново.
Можно ли хранить в хранилище таблицу значений или табличный документ?
Да, оба типа сериализуются. Табличный документ сохраняется со всем форматированием, таблица значений — со структурой колонок и данными. После Получить() вы получаете объект того же типа.
Чем ХранилищеЗначения отличается от ВременногоХранилища?
ХранилищеЗначения — тип данных для постоянного сохранения в полях базы. ВременноеХранилище — механизм серверной памяти для передачи данных между вызовами в течение сеанса, в базу оно не пишется и очищается по адресу или таймауту.
Стоит ли хранить файлы прямо в реквизите справочника?
Для небольших данных (подписи, печати, маленькие фото) — допустимо. Для большого числа файлов лучше отдельный регистр сведений или справочник присоединённых файлов: тогда списки и формы основного объекта не замедляются чтением тяжёлого поля.