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

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

Хранилище значения в 1С: что это и как с ним работать

Хранилище значения в 1С: что это за тип данных, как сохранить и извлечь файл, картинку или объект, примеры кода, ограничения и типичные ошибки разработчиков.

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

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

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

Что такое хранилище значения и зачем оно нужно

Хранилище значения (ValueStorage) — объект встроенного языка 1С, который «заворачивает» произвольное значение в специальный формат. При записи в базу платформа сериализует содержимое и сохраняет его в поле типа BLOB (двоичные данные). При чтении разработчик вызывает метод Получить() и получает исходное значение в первоначальном виде.

Типичные сценарии использования:

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

Без хранилища пришлось бы либо выносить файлы на диск и хранить пути (что ломается при переносе базы), либо кодировать данные в строки, что неудобно и неэффективно для бинарного контента.

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

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

На практике хранилище чаще всего добавляют в:

  • справочники — например, реквизит «ФайлКартинки» в «Номенклатура»;
  • регистры сведений — измерение «Объект», ресурс «Данные» типа ХранилищеЗначения (классический паттерн для присоединённых файлов);
  • константы — глобальные настройки в виде структуры;
  • планы обмена и задачи — для передачи параметров.

Для регистра сведений с хранилищем важно правильно выбрать измерения: по ним будет выполняться поиск и отбор данных. Само хранилище не может быть измерением — только ресурсом или реквизитом.

Как записать значение в хранилище

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

1c
// Сохранение картинки в реквизит справочника
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Images\photo.jpg");
ОбъектТовара = Справочники.Номенклатура.НайтиПоКоду("000001").ПолучитьОбъект();
ОбъектТовара.ФайлКартинки = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
ОбъектТовара.Записать();

Аналогично сохраняется таблица значений или структура с настройками:

1c
// Сохранение настроек в константу
Настройки = Новый Структура;
Настройки.Вставить("Колонка1", "Артикул");
Настройки.Вставить("Ширина", 120);
Константы.НастройкиОтчета.Установить(Новый ХранилищеЗначения(Настройки));

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

Как прочитать и извлечь данные из хранилища

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

1c
// Извлечение картинки из реквизита
Хранилище = ТоварСсылка.ФайлКартинки;
ДвоичныеДанные = Хранилище.Получить();
Если ДвоичныеДанные <> Неопределено Тогда
    ДвоичныеДанные.Записать("C:\Temp\photo.jpg");
КонецЕсли;

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

Ограничения и особенности хранилища значения

Несмотря на универсальность, у типа есть жёсткие ограничения, которые нужно учитывать при проектировании:

  • по содержимому хранилища нельзя строить отборы, сортировки и индексы в запросах — платформа видит только бинарное поле;
  • хранилище нельзя вывести напрямую в табличный документ или отчёт СКД как значение — сначала его нужно извлечь кодом;
  • нельзя сравнивать два хранилища на равенство штатными средствами запроса;
  • в тонком и веб-клиенте работа с файловой системой требует передачи данных через сервер или асинхронные методы помещения файлов.

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

Где хранить файлы

Хранилище значения в базе

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

Тома на диске (БСП)

Подходит для больших объёмов присоединённых файлов. Файлы лежат в каталоге, в базе — ссылки. Минус — нужно отдельно резервировать каталоги.

Строка неограниченной длины

Только для текстовых данных (XML, JSON). Для бинарных данных не подходит — потребуется Base64, что увеличивает объём примерно на треть.

Типичные ошибки при работе с хранилищем

Типичные ошибки
01
Вызов методов у результата Получить() без проверки на Неопределено — при пустом реквизите возникает ошибка выполнения.
02
Хранение ссылок на объекты базы внутри хранилища — после удаления объекта ссылка внутри контейнера превращается в битую, и сериализация может выдавать неожиданные результаты.
03
Игнорирование сжатия для крупных файлов — база растёт быстрее, чем могла бы.
04
Попытка отобрать данные запросом по содержимому хранилища — это невозможно, нужно дублировать ключевые атрибуты в обычных реквизитах.
05
Запись хранилища в транзакции с долгими операциями — большие бинарные данные увеличивают время блокировок.

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

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

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

Контрольные точки

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

Получить() после записи
Норма

Возвращает объект ожидаемого типа

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

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

Размер двоичных данных
Норма

Совпадает с размером исходного файла

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

Ноль или сильно меньше — файл не был прочитан до сохранения

Открытие извлечённого файла
Норма

Файл открывается штатной программой

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

Файл повреждён — нарушена передача данных между клиентом и сервером

Чтобы очистить хранилище, присвойте реквизиту пустое значение и запишите объект:

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

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

Итог

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

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

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

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

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

Почему Получить() возвращает Неопределено, хотя данные записывались?

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

Как вывести картинку из хранилища в форму или отчёт?

В управляемой форме добавьте реквизит типа Строка и поместите в него адрес временного хранилища: ПоместитьВоВременноеХранилище(ДвоичныеДанные, УникальныйИдентификатор), а элементу формы «Поле картинки» укажите этот реквизит. В табличном документе картинку вставляют через область рисунка, передавая объект Картинка, созданный из двоичных данных.

Сильно ли хранилище значения замедляет работу базы?

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

Чем хранилище значения отличается от временного хранилища?

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

Можно ли сжать данные при записи в хранилище?

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