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