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

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

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

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

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

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

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

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

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

Базовое чтение: метод Получить()

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

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

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

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

Сообщить("Хранилище пустое");

Иначе

// работаем с данными

КонецЕсли;

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

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

Чтение хранилища через запрос

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

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Файлы.ДанныеФайла КАК Хранилище

|ИЗ

| Справочник.Файлы КАК Файлы

|ГДЕ

| Файлы.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаФайл);

Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда

Данные = Выборка.Хранилище.Получить();

КонецЕсли;

Обратите внимание: запрос возвращает именно объект ХранилищеЗначения, поэтому Получить() всё равно обязателен. Распространённая ошибка — присвоить результат выборки напрямую в реквизит картинки или файла и получить сообщение о неверном типе.

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

Работа с типичным содержимым: картинки, файлы, настройки

Картинки

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

Картинка = Выборка.Фото.Получить();

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

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

КонецЕсли;

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

Файлы (двоичные данные)

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

ДвоичныеДанные = Выборка.ХранилищеФайла.Получить();

Если ТипЗнч(ДвоичныеДанные) = Тип("ДвоичныеДанные") Тогда

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

КонецЕсли;

Настройки и структуры

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

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

Проверка результата и диагностика проблем

Главные причины, по которым чтение хранилища «не работает»: поле пустое, данные испорчены, либо код ожидает другой тип содержимого. Диагностику удобно строить по шагам — сначала убедиться, что поле вообще заполнено, затем проверить тип извлечённого объекта:

Данные = Выборка.Хранилище.Получить();

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

Сообщить("Хранилище пустое");

Иначе

Сообщить("Тип содержимого: " + ТипЗнч(Данные));

КонецЕсли;

Отдельный случай — ошибка «Ошибка формата потока» при вызове Получить(). Она означает, что сериализованные данные повреждены: например, запись в базу была прервана, или поле заполнялось внешними средствами напрямую в СУБД. Восстановить такое содержимое штатными средствами обычно нельзя — данные придётся загружать заново из источника.

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

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

Результат Получить()
Норма

Объект ожидаемого типа (Картинка, ДвоичныеДанные, Структура)

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

Неопределено — хранилище пустое или не заполнялось

ТипЗнч(Данные)
Норма

Совпадает с типом, который записывался

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

Другой тип — данные записаны другой версией кода или конфигурации

Вызов Получить()
Норма

Выполняется без исключений

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

Ошибка формата потока — данные повреждены

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

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

Третья группа проблем связана с производительностью. Поля хранилищ тяжёлые: выбирать их запросом «на всякий случай» в списках и отчётах не стоит — это замедляет выборку и увеличивает трафик между сервером СУБД и сервером 1С. В динамических списках и отчётах СКД поле хранилища лучше вообще исключать из выборки, а читать только по требованию для конкретной строки.

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

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

Чтение хранилищ в типовых конфигурациях и через консоль

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

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

FAQ

Почему Получить() возвращает Неопределено, хотя поле выглядит заполненным?

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

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

Штатных средств для этого нет: тип содержимого становится известен только после вызова Получить() и проверки ТипЗнч(). Косвенно можно ориентироваться на прикладную логику — например, по имени реквизита и коду, который его записывает.

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

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

Что делать при ошибке «Ошибка формата потока» при чтении?

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

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

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

Читается ли хранилище значений на тонком клиенте и в веб-клиенте?

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

Итог

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

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