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

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

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

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

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

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

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

Что такое хранилище значений и когда оно нужно

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

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

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

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

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

Если данных много и они крупные (сканы, архивы), рассмотрите вынос их в отдельный регистр сведений «ДвоичныеДанные» со ссылкой на владельца. Тогда основная таблица справочника остаётся лёгкой, а тяжёлые BLOB читаются только по требованию.

Запись данных в хранилище

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

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

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

Объект.Фотография = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));

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

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

Аналогично сохраняются и объекты встроенного языка — таблицы значений, структуры, настройки компоновки данных. Главное условие: объект должен быть сериализуемым. Ссылки на объекты базы внутри структуры сериализуются корректно, а вот, например, COM-объекты и открытые файловые потоки сохранить нельзя.

Порядок записи значения
  1. 1
    Получите объект базы методом ПолучитьОбъект() по ссылке
  2. 2
    Подготовьте значение: двоичные данные, картинку или структуру
  3. 3
    Присвойте реквизиту Новый ХранилищеЗначения(Значение, Сжатие)
  4. 4
    Вызовите Записать() и убедитесь в отсутствии исключений
  5. 5
    Откройте элемент в режиме 1С:Предприятие и проверьте извлечение данных

Чтение и извлечение значений

Извлечение выполняется методом Получить(). Важно помнить: реквизит хранит объект-хранилище, а не сами данные, поэтому обращение к реквизиту без Получить() вернёт именно контейнер:

Хранилище = СсылкаНаЭлемент.Фотография; // это ХранилищеЗначения

Данные = Хранилище.Получить(); // а это уже сами данные

Если Данные <> Неопределено Тогда

Данные.Записать("C:\Temp\photo.jpg");

КонецЕсли;

Всегда проверяйте результат на Неопределено: пустое хранилище возвращает именно его. Попытка вызвать метод у результата без проверки — частая причина ошибок «Значение не является значением объектного типа».

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

bsl
Данные = Ссылка.Файл.Получить();
Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
    Адрес = ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
КонецЕсли;

Ограничения и влияние на производительность

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

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

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

Самая распространённая ошибка — сравнение или присвоение хранилища напрямую. Конструкция Если Объект1.Файл = Объект2.Файл Тогда сравнивает контейнеры, а не содержимое, и почти всегда даёт ложь. Для сравнения содержимого нужно извлечь оба значения и сравнить, например, по хеш-сумме.

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

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

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

Альтернативы хранилищу значений

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

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

Сравнение вариантов хранения

ХранилищеЗначения

Универсально: любые сериализуемые данные. Ограничение — нет поиска по содержимому и рост объёма базы

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

Подходит для XML/JSON/текста, читается в консоли. Не подходит для двоичных данных

Присоединенные файлы (БСП)

Готовый механизм версий и томов. Требует наличия библиотеки стандартных подсистем в конфигурации

Внешние тома / сетевые каталоги

Снимают нагрузку с базы при больших файлах. Нужна отдельная забота о резервном копировании и правах доступа

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

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

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

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

Извлечённый файл
Норма

Открывается штатной программой, размер совпадает с исходным

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

Файл не открывается, размер нулевой или отличается

Пустой элемент
Норма

Получить() возвращает Неопределено, код отрабатывает без исключений

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

Ошибка обращения к методам Неопределено

Скорость списка
Норма

Список элементов открывается быстро, хранилища не читаются

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

Список тормозит из-за извлечения BLOB для каждой строки

При разработке удобно сделать в форме элемента кнопку «Показать содержимое», которая выводит результат Получить() в сообщение или открывает файл. Это сильно ускоряет отладку по сравнению с каждым разом писать код в консоли.
ПЗ
Практическое замечание

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

Можно ли найти элемент по содержимому хранилища значений?

Напрямую — нет: платформа не индексирует и не сравнивает содержимое BLOB-полей в запросах. Обходной путь — хранить рядом служебные реквизиты (имя файла, хеш-сумму, тип данных) и искать по ним. Хеш также позволяет находить дубликаты файлов.

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

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

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

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

Почему база быстро растёт после добавления хранения картинок?

Каждая картинка сохраняется целиком в таблице базы, а при обновлении реквизита старая версия может оставаться в журналах и избыточных страницах СУБД до сжатия. Снижают рост сжатие данных при записи, ограничение разрешения загружаемых изображений и вынос файлов в тома вне базы.

Как перенести данные из хранилища в другую базу?

Через типовые механизмы обмена (Конвертацию данных, универсальный обмен XML) хранилища переносятся как сериализованное значение — стандартные правила обрабатывают этот тип. При самописной выгрузке извлекайте значение методом Получить() и сохраняйте в файл, а в базе-приёмнике создавайте новое хранилище из загруженных данных.

Итог

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

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