Метод Получить() и всё вокруг него: достаём данные из ХранилищеЗначения в 1С 8.3

· 10 мин

Содержание

Чтобы прочитать хранилище значений в 1С, вызовите у него метод Получить(): Данные = Объект.Настройки.Получить();. Метод вернёт то, что туда положили при записи: таблицу значений, структуру, картинку, двоичные данные или Неопределено, если хранилище пустое. Весь код чтения выполняется на сервере.

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

Как устроено хранилище значения и что в нём лежит

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

Что обычно хранят:

  • 📄 файлы и вложения — как ДвоичныеДанные;
  • 🖼️ фото товаров и сотрудников — как Картинка или ДвоичныеДанные;
  • 📊 настройки отчётов и обработок — как Структура, Соответствие, НастройкиКомпоновкиДанных;
  • 🗂️ промежуточные таблицы — как ТаблицаЗначений или Массив;
  • 🔑 служебные параметры обмена и подключений.

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

При записи разработчик может включить сжатие:

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

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

Где лежит хранилищеКак добраться до значения
Реквизит объектаОбъект.Реквизит.Получить()
КонстантаКонстанты.ИмяКонстанты.Получить().Получить()
Ресурс регистра сведенийзапрос или МенеджерЗаписи.Ресурс.Получить()
Поле в результате запросаВыборка.Поле.Получить()

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

Хранилище значения и хранилище настроек — разные вещи. ХранилищеОбщихНастроек.Загрузить(КлючОбъекта, КлючНастроек) возвращает уже готовое значение, вызывать у результата Получить() не нужно.

Базовый алгоритм чтения хранилища значений

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

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

Пример универсальной функции для общего модуля:

Функция ПрочитатьХранилище(Хранилище, ОжидаемыйТип = Неопределено) Экспорт

    Если ТипЗнч(Хранилище) <> Тип("ХранилищеЗначения") Тогда
        Возврат Неопределено;
    КонецЕсли;

    Значение = Хранилище.Получить();

    Если Значение = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;

    Если ОжидаемыйТип <> Неопределено И ТипЗнч(Значение) <> ОжидаемыйТип Тогда
        Возврат Неопределено;
    КонецЕсли;

    Возврат Значение;

КонецФункции

Вызов выглядит так:

Таблица = ПрочитатьХранилище(Объект.Настройки, Тип("ТаблицаЗначений"));
Если Таблица = Неопределено Тогда
    Таблица = Новый ТаблицаЗначений;
КонецЕсли;

Если вы не знаете, что лежит в хранилище чужой конфигурации, поставьте точку останова после Получить() и откройте значение в окне «Вычислить выражение» отладчика. Там видно и тип, и состав колонок или ключей структуры.

Приведение типов после чтения

Типовые ситуации и что с ними делать:

Что вернул Получить()Что делать дальше
ТаблицаЗначенийработать напрямую, перебирать строки
Массив из Структурасобрать таблицу значений (пример ниже)
Структурачитать поля через Свойство()
ДвоичныеДанныезаписать в файл через Записать() или поместить во временное хранилище
Картинкапоказать на форме через временное хранилище
Строка с XML или JSONразобрать через ЧтениеXML или ЧтениеJSON

Со структурой не обращайтесь к полю напрямую, если не уверены, что оно есть. Обращение к отсутствующему ключу даёт ошибку, а Свойство() просто вернёт Ложь:

Параметры = Хранилище.Получить();
АдресСервера = "";
Если ТипЗнч(Параметры) = Тип("Структура") Тогда
    Параметры.Свойство("АдресСервера", АдресСервера);
КонецЕсли;

Как превратить содержимое хранилища в таблицу значений

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

Если внутри уже ТаблицаЗначений, ничего конвертировать не нужно. Хорошая привычка — проверить, что нужные колонки на месте. Хранилище могли записать старой версией обработки, и колонок там будет меньше:

Таблица = Хранилище.Получить();
Если Таблица.Колонки.Найти("Сумма") = Неопределено Тогда
    Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
КонецЕсли;

Если внутри массив структур, таблицу собирают по ключам первой структуры:

Функция МассивСтруктурВТаблицу(МассивСтруктур) Экспорт

    Таблица = Новый ТаблицаЗначений;
    Если МассивСтруктур.Количество() = 0 Тогда
        Возврат Таблица;
    КонецЕсли;

    Для Каждого КлючИЗначение Из МассивСтруктур[0] Цикл
        Таблица.Колонки.Добавить(КлючИЗначение.Ключ);
    КонецЦикла;

    Для Каждого Элемент Из МассивСтруктур Цикл
        ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Элемент);
    КонецЦикла;

    Возврат Таблица;

КонецФункции

ЗаполнитьЗначенияСвойств переносит совпадающие по имени поля и молча пропускает лишние. Если у части структур есть ключи, которых нет в первой, они потеряются. Тогда соберите полный список ключей отдельным проходом по массиву.

Если внутри Соответствие, получается таблица из двух колонок:

Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Ключ");
Таблица.Колонки.Добавить("Значение");
Для Каждого Пара Из Соответствие Цикл
    НоваяСтрока = Таблица.Добавить();
    НоваяСтрока.Ключ = Пара.Ключ;
    НоваяСтрока.Значение = Пара.Значение;
КонецЦикла;

Колонки, созданные без описания типов, принимают значения любого типа. Для вывода на форму и для запроса к таблице этого мало: задайте ОписаниеТипов для каждой колонки, иначе поле на форме не поймёт, что показывать, а ПОМЕСТИТЬ во временную таблицу даст ошибку.

Чтение через БСП: безопасное хранилище и реквизиты объекта

В типовых конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и другие) не всё, что похоже на хранилище, читается напрямую.

Пароли и токены — только через безопасное хранилище

Пароли к интернет-поддержке, учётные записи почты, ключи доступа к сервисам БСП держит не в реквизитах, а в отдельном безопасном хранилище данных. Читать его надо функцией общего модуля и в привилегированном режиме:

УстановитьПривилегированныйРежим(Истина);
Пароль = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(Ссылка, "Пароль");
УстановитьПривилегированныйРежим(Ложь);

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

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

Один реквизит без загрузки всего объекта

Чтобы прочитать хранилище у ссылки, не открывайте объект целиком. Возьмите только нужный реквизит:

Хранилище = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Настройки");
Данные = ?(Хранилище = Неопределено, Неопределено, Хранилище.Получить());

Функция выполняет запрос к одному полю и возвращает объект ХранилищеЗначения, а не его содержимое. Вызов Получить() по-прежнему за вами. Для нескольких реквизитов есть ЗначенияРеквизитовОбъекта — она вернёт структуру.

Настройки пользователей

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

Пустое хранилище, неизвестный тип и другие ошибки чтения

Хранилище редко ломает базу, но регулярно ломает код, который его читает. Разберите симптом по списку:

Диагностика: выберите симптом

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

Ещё один подвох — объекты, которые нельзя сериализовать. Например, Запрос или ЧтениеXML в хранилище не положить: ошибка возникнет при записи, а не при чтении. Если вы разбираете чужой код и видите, что хранилище всегда пустое, проверьте, не падает ли запись молча внутри Попытка ... Исключение.

Скорость: почему хранилище тормозит запросы

Поле типа «Хранилище значения» хранится в базе как двоичные данные неограниченной длины. Отсюда три правила.

1. Не выбирайте это поле без нужды. Запрос ВЫБРАТЬ * или выборка всех реквизитов списка тянет с сервера СУБД все вложения подряд. Список из 5 000 товаров с фото по 300 КБ — это около 1,5 ГБ трафика ради одного отчёта.

2. Не ставьте поле в условия. По хранилищу нельзя отбирать, группировать и упорядочивать. Если нужен отбор «есть файл / нет файла», заведите рядом булев реквизит и заполняйте его при записи.

3. Читайте хранилище точечно. Сначала лёгким запросом получите список ссылок, потом отдельным запросом заберите хранилище только для нужных:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Т.Ссылка КАК Ссылка,
|    Т.Настройки КАК Настройки
|ИЗ
|    Справочник.ПрофилиВыгрузки КАК Т
|ГДЕ
|    Т.Ссылка В (&Ссылки)";
Запрос.УстановитьПараметр("Ссылки", МассивСсылок);

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Данные = Выборка.Настройки.Получить();
    // обработка данных
КонецЦикла;

Справочник.ПрофилиВыгрузки здесь условный — подставьте свой объект.

Сравнение способов доступа к хранилищу у ссылки:

СпособЧто читается из базыКогда подходит
Ссылка.ПолучитьОбъект()весь объект с табличными частяминужно ещё и изменить объект
Ссылка.Настройки (через точку)весь объект целикомразовая проверка в отладке
ЗначениеРеквизитаОбъектаодно полечтение у одной ссылки
Запрос по массиву ссылокодно поле у многих объектовпакетная обработка

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

Хранилище значений в управляемой форме

В тонком и веб-клиенте типа ХранилищеЗначения нет. Отсюда главное правило управляемых форм: хранилище читают и пишут только на сервере, а на клиент передают уже распакованное значение подходящего типа.

Чтение при открытии и запись при сохранении

Для формы объекта используйте пару событий модуля формы:

&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    Таблица = ТекущийОбъект.Настройки.Получить();
    Если ТипЗнч(Таблица) = Тип("ТаблицаЗначений") Тогда
        ЗначениеВРеквизитФормы(Таблица, "ТаблицаНастроек");
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    Таблица = РеквизитФормыВЗначение("ТаблицаНастроек");
    ТекущийОбъект.Настройки = Новый ХранилищеЗначения(Таблица, Новый СжатиеДанных(9));
КонецПроцедуры

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

Если состав колонок заранее неизвестен, их добавляют программно методом формы ИзменитьРеквизиты() в ПриСозданииНаСервере, а затем создают элементы формы через Элементы.Добавить().

Картинки и файлы

Картинку из хранилища на форму передают через временное хранилище, а не напрямую:

&НаСервере
Процедура ЗагрузитьФото()
    ДанныеКартинки = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Объект.Ссылка, "Фото");
    Если ДанныеКартинки <> Неопределено Тогда
        АдресФото = ПоместитьВоВременноеХранилище(ДанныеКартинки.Получить(), УникальныйИдентификатор);
    КонецЕсли;
КонецПроцедуры

АдресФото — строковый реквизит формы, на него ставят поле с видом «Поле картинки». Второй параметр УникальныйИдентификатор привязывает временные данные к форме: при её закрытии они удаляются сами.

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

Перед выпуском формы с хранилищем
Выполнено: 0 / 5

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

Можно ли прочитать хранилище значений SQL-запросом напрямую из СУБД?
Нет смысла. В таблице лежит двоичный блок во внутреннем формате платформы, часто сжатый. Надёжный путь — внешнее соединение, COM или HTTP-сервис, где данные читает сама 1С.
Как выгрузить содержимое хранилища в файл для анализа?
Прочитайте значение через Получить() и запишите его в XML через СериализаторXDTO, если тип сериализуемый. Двоичные данные сохраняйте методом Записать() с нужным расширением.
Влияет ли уровень сжатия на скорость чтения?
Немного: распаковка занимает процессорное время на сервере, зато из базы передаётся меньше байт. Для таблиц и текстов сжатие окупается, для JPEG и PDF почти бесполезно — они уже сжаты.
Можно ли изменить данные внутри хранилища без перезаписи?
Нет. Прочитайте значение, измените его в памяти и создайте новый объект ХранилищеЗначения. Затем присвойте его реквизиту и запишите объект.
Почему в обработке «Консоль запросов» поле хранилища показывается пустым или как «Хранилище значения»?
Консоль выводит само поле, а не содержимое. Чтобы увидеть данные, вызовите Получить() в коде или в окне «Вычислить выражение» отладчика.