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