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

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

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

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

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

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

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

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

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

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

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

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

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

// Запись

ТЗ = Новый ТаблицаЗначений;

ТЗ.Колонки.Добавить("Товар");

ТЗ.Колонки.Добавить("Цена");

// ... заполнение таблицы ...

ОбъектСправочника = Справочники.НастройкиОбмена.СоздатьЭлемент();

ОбъектСправочника.Наименование = "Настройки выгрузки";

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

ОбъектСправочника.Записать();

// Чтение

Выборка = Справочники.НастройкиОбмена.Выбрать();

Пока Выборка.Следующий() Цикл

СохранённыеДанные = Выборка.ДанныеНастройки.Получить();

Если ТипЗнч(СохранённыеДанные) = Тип("ТаблицаЗначений") Тогда

// работаем с таблицей

КонецЕсли;

КонецЦикла;

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

bsl
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Файлы\отчет.pdf");
Объект.ФайлВложение = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.Записать();

Работа с картинками и файлами

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

// Помещение файла картинки

ДвоичныеДанные = Новый ДвоичныеДанные(ИмяФайлаНаДиске);

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

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

// Вывод картинки в форму

ДанныеКартинки = ТоварОбъект.Фото.Получить();

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

АдресВХранилище = ПоместитьВоВременноеХранилище(ДанныеКартинки, УникальныйИдентификатор);

// АдресВХранилище назначается элементу формы типа "Поле картинки"

КонецЕсли;

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

Ограничения типа в запросах и отчётах

Поле типа ХранилищеЗначения нельзя использовать в условиях ГДЕ, соединениях, группировках и упорядочивании запроса. Платформа просто не умеет сравнивать сериализованные данные. В запросе такое поле можно только выбрать, после чего работать с ним в коде через Получить().

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

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

Когда использовать хранилище значения, а когда нет

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

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

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

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

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

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

Подходит для документов, сканов, больших вложений. Даёт версии, подписи, тома хранения. Ограничение: зависимость от БСП.

Обычные типизированные реквизиты

Подходят для любых данных, участвующих в поиске, отборах и отчётах. Ограничение: фиксированный состав типов.

Типичные ошибки и как их избежать

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

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

Третья ошибка — чтение хранилища в цикле по большой выборке. Каждый вызов Получить() десериализует данные заново. Если нужно обработать много объектов, читайте хранилище только там, где оно действительно требуется, и не выбирайте это поле «на всякий случай».

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

Проверка результата и отладка

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

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

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

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

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

Возвращается объект ожидаемого типа (ТаблицаЗначений, ДвоичныеДанные и т.д.)

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

Неопределено или значение другого типа

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

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

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

Ноль или существенно меньше ожидаемого

Картинка в форме
Норма

Изображение отображается после помещения во временное хранилище

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

Пустое поле картинки или ошибка формата

Итог

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

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

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

Можно ли найти все объекты, где хранилище значения заполнено?

Напрямую в запросе — нет, содержимое хранилища не сравнивается. Практические варианты: завести отдельный булев реквизит-признак «ЕстьВложение» и поддерживать его при записи, либо перебрать объекты в коде с проверкой Получить() <> Неопределено, что допустимо только на небольших объёмах данных.

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

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

Как сохранить в хранилище структуру с настройками?

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

Влияет ли сжатие данных на скорость работы?

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

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

Наиболее вероятная причина — несовпадение состава типов в конфигурациях: сериализованный объект ссылается на тип, которого нет в базе-приёмнике. Решение — хранить передаваемые данные в универсальном формате (XML, JSON) в виде строки и парсить их на стороне получателя.

Можно ли хранить в одном реквизите и картинки, и файлы?

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