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

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

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

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

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

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

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

Что такое хранилище значений и когда его применять

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

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

Базовый синтаксис: как поместить значение

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

Картинка = Новый Картинка("C:\Images\photo.jpg");

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

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

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

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

Чтение выполняется методом Получить(). Если реквизит не заполнен, метод вернёт Неопределено, поэтому перед использованием результата стоит делать проверку:

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

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

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

КонецЕсли;

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

Что можно поместить в хранилище

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

Есть и ограничения. Нельзя сохранить объекты, привязанные к контексту сеанса: например, COMОбъект, элементы формы, фоновые задания. Запрос и построитель запроса как объект тоже не сериализуются — вместо них сохраняют результат запроса (Запрос.Выполнить().Выгрузить()).

Что сохраняемМожно лиКомментарий
ДвоичныеДанные, КартинкаДаСтандартный способ хранения файлов и изображений
ТаблицаЗначений, ДеревоЗначенийДаПолностью восстанавливаются при Получить()
Структура, Соответствие, МассивДаВложенные коллекции тоже сериализуются
Ссылки на объектыДаБитая ссылка при удалении объекта
COMОбъект, элементы формыНетОшибка сериализации при записи
Результат запросаДаСохраняйте выгрузку, а не сам запрос

Типовые сценарии с примерами кода

Сохранение файла с диска

Файл читается в двоичные данные и помещается в хранилище. Параллельно полезно сохранить имя файла в отдельном строковом реквизите — из хранилища оно не восстанавливается:

ДвоичныеДанные = Новый ДвоичныеДанные("C:\Docs\договор.pdf");

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

Объект.ИмяФайла = "договор.pdf";

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

Обратное действие — выгрузка файла на диск:

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

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

ДвоичныеДанные.Записать("C:\Temp\" + Объект.ИмяФайла);

КонецЕсли;

Сохранение произвольной структуры настроек

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

Настройки = Новый Структура;

Настройки.Вставить("Сервер", "192.168.0.10");

Настройки.Вставить("Порт", 8080);

Настройки.Вставить("Таймаут", 30);

МенеджерЗаписи = РегистрыСведений.НастройкиОбмена.СоздатьМенеджерЗаписи();

МенеджерЗаписи.Узел = УзелСсылка;

МенеджерЗаписи.Параметры = Новый ХранилищеЗначения(Настройки);

МенеджерЗаписи.Записать();

Хранилище в константе

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

Константы.ОбщиеНастройки.Установить(Новый ХранилищеЗначения(СтруктураНастроек));

Настройки = Константы.ОбщиеНастройки.Получить().Получить();

Хранилище значений и временное хранилище: не путать

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

Временное хранилище живёт ограниченное время (до завершения сеанса или перезапуска сервера) и не предназначено для долговременного хранения. Типичная связка в управляемых формах: файл с клиента помещается во временное хранилище, на сервере извлекается по адресу через ПолучитьИзВременногоХранилища(Адрес) и уже затем записывается в реквизит типа ХранилищеЗначения.

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

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

Постоянное хранение в базе, переживает перезапуск. Подходит для файлов, настроек, вложений.

Временное хранилище

Передача данных между клиентом и сервером в рамках сеанса. Данные удаляются автоматически.

Табличная часть / отдельный регистр

Когда по данным нужен поиск, отбор или связи — хранилище для этого не подходит.

Ограничения и риски

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

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

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

Проверка результата и типичные ошибки

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

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

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

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

Возвращается объект исходного типа с корректным содержимым

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

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

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

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

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

Нулевой размер — файл не был прочитан перед записью

Запись объекта
Норма

Записать() выполняется без исключений

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

Ошибка сериализации — в значении неподдерживаемый тип

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

FAQ

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

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

Как узнать, что лежит внутри хранилища, не открывая код?

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

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

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

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

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

Чем хранилище значений отличается от реквизита типа Строка неограниченной длины?

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

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

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

Итог

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

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