ХранилищеЗначения в 1С 8.3: чтение данных кодом, отладчиком и через двоичные данные
Содержание
Значение из хранилища значений 1С получают одним методом: Хранилище.Получить(). Он возвращает то, что туда положили, — таблицу, структуру, двоичные данные файла или Неопределено, если хранилище пустое. Вызывать его нужно на сервере: в тонком клиенте типа ХранилищеЗначения нет.
Частая ошибка — искать у хранилища метод Загрузить(). Такой метод есть у хранилищ настроек (ХранилищеОбщихНастроек.Загрузить(...)), а у объекта ХранилищеЗначения его нет. Отсюда половина вопросов на форумах: код из примера про настройки не работает
с реквизитом справочника.
Что лежит внутри хранилища значений и откуда его читать
ХранилищеЗначения — это контейнер. В него упаковывают значение почти любого типа, который платформа умеет сериализовать, и записывают в реквизит объекта или ресурс регистра. В базе данных это поле неограниченной длины.
Что обычно хранят:
- 📄 сканы договоров, PDF и другие файлы — как
ДвоичныеДанные; - 🖼 фото товаров и подписи — тоже двоичные данные или объект
Картинка; - 📊 таблицы значений и структуры — сохранённые настройки, кэш расчётов, параметры обмена;
- 🔗 массивы ссылок на элементы справочников.
Хранилище упаковывает значение целиком. Значит, нельзя прочитать «кусочек» — например, одну строку таблицы. Сначала Получить(), потом работа с результатом.
| Где находится поле | Как до него добраться |
|---|---|
| Реквизит справочника или документа | запрос по ссылке или Ссылка.ПолучитьОбъект() на сервере |
| Ресурс регистра сведений | запрос к регистру или МенеджерЗаписи.Прочитать() |
| Реквизит табличной части | запрос к табличной части или обход строк объекта |
| Значение в XML-выгрузке | СериализаторXDTO — хранилище записано строкой Base64 |
В запросе поле типа ХранилищеЗначения можно выбрать, но нельзя использовать в условии ГДЕ, в сортировке и группировке. Отбирайте записи по другим полям, а содержимое проверяйте уже в коде.
Способ 1. Посмотреть содержимое хранилища вручную через отладчик
Иногда код писать не нужно: надо просто понять, что лежит в реквизите конкретного документа. В пользовательском режиме хранилище выглядит как пустое или нередактируемое поле — содержимое там не показывается. Удобнее всего заглянуть через отладчик конфигуратора.
- Откройте конфигуратор и запустите отладку через меню
Отладка → Начать отладку. Для клиент-серверной базы на сервере 1С должна быть включена отладка (агент сервера запускают с ключом-debug). - Найдите модуль, где объект уже прочитан на сервере, — например, обработчик
ПриСозданииНаСервереформы документа. Поставьте точку останова клавишейF9. - В пользовательском режиме откройте нужный документ. Выполнение остановится на точке.
- Нажмите
Shift+F9(вычислить выражение) и введитеОбъект.ИмяРеквизита.Получить(), где вместоИмяРеквизита— имя реквизита-хранилища. - Раскройте результат: у таблицы видны колонки и строки, у структуры — ключи, у двоичных данных — размер.
Не ставьте точки останова в рабочей базе в разгар дня — каждый пользователь, который откроет документ, будет ждать, пока вы отпустите отладчик. Работайте на копии базы.
Второй ручной вариант — внешняя обработка выполнения произвольного кода, которую администратор запускает с полными правами. В ней достаточно выполнить на сервере две строки:
Объект = Документы.ПоступлениеТоваровУслуг.НайтиПоНомеру("0000-000123", ТекущаяДата()).ПолучитьОбъект();
Сообщить(ТипЗнч(Объект.ИмяРеквизита.Получить()));
Имя документа и реквизита здесь — пример, подставьте свои. Поиск по номеру с датой работает, если нумерация периодическая; иначе ищите документ запросом.
Способ 2. Метод Получить() в коде конфигурации
Это основной путь — его используют в модулях форм, обработках и расширениях. Чтобы не тянуть в память весь объект, читайте поле запросом.
&НаСервереБезКонтекста
Функция ДанныеИзХранилища(Ссылка)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.ДополнительныеДанные КАК ДополнительныеДанные
|ИЗ
| Справочник.Договоры КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Не Выборка.Следующий() Тогда
Возврат Неопределено;
КонецЕсли;
Возврат Выборка.ДополнительныеДанные.Получить();
КонецФункции
Справочник.Договоры и реквизит ДополнительныеДанные — условные имена. Функция возвращает значение уже распакованным. Если там таблица значений, на клиент её передать не получится — обработайте её на сервере и верните клиенту строку, число или массив.
Как записать значение, чтобы потом его прочитать
Без правильной записи читать нечего. Значение всегда оборачивают в конструктор:
ДокОбъект = Ссылка.ПолучитьОбъект();
ДокОбъект.ДополнительныеДанные = Новый ХранилищеЗначения(Таблица, Новый СжатиеДанных(9));
ДокОбъект.Записать();
Второй параметр — сжатие, уровень от 0 до 9. Для таблиц и текста оно заметно уменьшает объём, для JPEG и ZIP почти бесполезно: эти форматы уже сжаты.
Получить() возвращает копию. Если вы добавили строку в полученную таблицу, в базе ничего не изменится. Нужно снова создать Новый ХранилищеЗначения(...), присвоить его реквизиту и записать объект.
Ресурс регистра сведений
Для регистра логика та же. Пример через менеджер записи:
МенеджерЗаписи = РегистрыСведений.ДанныеОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = Ссылка;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
Значение = МенеджерЗаписи.Данные.Получить();
КонецЕсли;
Имя регистра и измерения — снова пример. Метод Выбран() защищает от случая, когда записи по ключу нет.
Способ 3. Без файла: временное хранилище и строка внутреннего формата
Название «хранилище» в 1С носят три разные вещи, и их часто путают:
| Механизм | Где живут данные | Чем читать |
|---|---|---|
ХранилищеЗначения | в поле базы данных, постоянно | .Получить() |
| Временное хранилище | в памяти сеанса, пока жива форма или сеанс | ПолучитьИзВременногоХранилища(Адрес) |
| Хранилища настроек | системные таблицы настроек | ХранилищеОбщихНастроек.Загрузить(...) |
Временное хранилище — мост между сервером и клиентом. Типичный сценарий: достали значение из реквизита на сервере, положили во временное хранилище, а клиент забрал его по адресу.
&НаСервере
Функция АдресДанных(Ссылка)
Значение = ДанныеИзХранилища(Ссылка);
Возврат ПоместитьВоВременноеХранилище(Значение, УникальныйИдентификатор);
КонецФункции
&НаКлиенте
Процедура ПоказатьДанные(Команда)
Адрес = АдресДанных(Объект.Ссылка);
Данные = ПолучитьИзВременногоХранилища(Адрес);
КонецПроцедуры
Привязка к УникальныйИдентификатор формы означает: данные удалятся, когда форма закроется. Для двоичных данных это удобно, а вот таблицу значений клиент не поймёт — передавайте на клиент только то, что он умеет обрабатывать.
Передать хранилище строкой
Иногда хранилище надо переслать целиком — в другую базу, в текстовое поле, в веб-сервис. Для этого есть строка внутреннего формата:
Строка = ЗначениеВСтрокуВнутр(Хранилище);
// ... передали строку ...
ХранилищеКопия = ЗначениеИзСтрокиВнутр(Строка);
Значение = ХранилищеКопия.Получить();
Обе функции работают на сервере и в толстом клиенте. Строка годится для обмена между базами 1С, но не для людей и не для чужих систем.
Хранилище в XML-файле обмена
Если вы разбираете выгрузку XML, хранилище там записано как строка Base64. Читайте его штатным сериализатором, а не вручную: СериализаторXDTO.ПрочитатьXML(ЧтениеXML) вернёт готовый объект ХранилищеЗначения, у которого затем вызывают Получить().
Способ 4. Двоичные данные: извлечь файл или картинку из хранилища
Самый частый практический случай — в реквизите лежит скан или фото, и его надо показать на форме или отдать пользователю файлом.
Показать картинку на форме
- Добавьте на форму реквизит формы
АдресКартинкис типом «Строка». - Разместите его на форме полем вида «Поле картинки».
- При открытии формы заполните адрес на сервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ДД = Объект.Ссылка.Фото.Получить();
Если ТипЗнч(ДД) = Тип("ДвоичныеДанные") Тогда
АдресКартинки = ПоместитьВоВременноеХранилище(ДД, УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
Обращение через точку Объект.Ссылка.Фото здесь допустимо для одного элемента. В цикле по сотням записей так не делайте — об этом ниже.
Сохранить файл на диск
На сервере достаточно метода двоичных данных:
ДД = Хранилище.Получить();
Если ТипЗнч(ДД) = Тип("ДвоичныеДанные") Тогда
ДД.Записать("D:\Выгрузка\scan_123.pdf");
КонецЕсли;
Путь здесь — путь на сервере 1С, а не на компьютере бухгалтера. Учётной записи службы сервера нужны права на запись в эту папку.
Чтобы файл скачал сам пользователь, двоичные данные помещают во временное хранилище, а на клиенте вызывают получение файла по адресу. В современных версиях платформы для этого есть асинхронный метод НачатьПолучениеФайлаССервера; старый синхронный ПолучитьФайл в веб-клиенте с отключённым режимом модальности не работает.
Перед записью узнайте размер: ДД.Размер() возвращает число байт. Если там 0 — файл сохранили пустым ещё при записи, искать ошибку надо в коде загрузки.
Способ 5. Проверка типа извлечённого значения
Хранилище не помнит, какой тип туда положили, а реквизит его тоже не ограничивает. Через год в одном и том же поле могут оказаться структура старого формата, таблица нового и Неопределено у новых элементов. Поэтому после Получить() тип проверяют всегда.
Функция ПрочитатьНастройки(Хранилище)
Если ТипЗнч(Хранилище) <> Тип("ХранилищеЗначения") Тогда
Возврат Неопределено; // NULL из левого соединения
КонецЕсли;
Значение = Хранилище.Получить();
Если Значение = Неопределено Тогда
Возврат Неопределено;
ИначеЕсли ТипЗнч(Значение) = Тип("Структура") Тогда
Если Значение.Свойство("Версия") И Значение.Версия = 2 Тогда
Возврат Значение;
КонецЕсли;
Возврат ПреобразоватьСтаруюСтруктуру(Значение);
ИначеЕсли ТипЗнч(Значение) = Тип("ТаблицаЗначений") Тогда
Возврат Новый Структура("Версия, Таблица", 2, Значение);
КонецЕсли;
Возврат Неопределено;
КонецФункции
ПреобразоватьСтаруюСтруктуру — ваша функция миграции. Приём с ключом Версия стоит заложить сразу при записи: потом формат данных можно менять без потери старых значений.
Что ещё проверить после чтения:
- ✅ у таблицы есть нужные колонки —
Значение.Колонки.Найти("Сумма") <> Неопределено; - ✅ ссылки внутри не «битые» — объект мог быть удалён, а хранилище об этом не знает;
- ✅ у двоичных данных размер больше нуля.
Поиск ссылок и удаление помеченных объектов не заглядывают внутрь хранилища. Элемент справочника, ссылка на который лежит только в хранилище, удалится без предупреждения, и при чтении вы получите <Объект не найден>.
Большие хранилища значений: как не замедлить базу
Пока в хранилище лежат настройки на пару килобайт, о скорости думать не нужно. Проблемы начинаются со сканами по 5–10 МБ в реквизите справочника или документа.
Почему тормозит:
- 🐢 объект читается целиком — открыли форму документа ради номера, а с сервера пришли мегабайты скана;
- 🐢 обращение через точку
Ссылка.Реквизиттоже читает объект полностью; - 🐢 каждая перезапись документа (например, перепроведение) заново пишет всё поле;
- 🐢 база и резервные копии растут быстрее, чем кажется.
Что делать:
- Выносите тяжёлые данные в отдельный регистр сведений с измерением-владельцем. Документ остаётся лёгким, а файл читают только тогда, когда он нужен.
- Читайте запросом только нужное поле, а не объект целиком. В типовых конфигурациях на БСП для этого есть
ОбщегоНазначения.ЗначениеРеквизитаОбъекта— он тоже работает через запрос. - Включайте сжатие для текста и таблиц (
Новый СжатиеДанных(9)). Для уже сжатых форматов уровень 0 сэкономит процессорное время. - Не храните файлы в базе, если их много. Для десятков тысяч сканов надёжнее файловое хранилище на диске или в томах, а в базе — только ссылка.
| Подход | Когда подходит | Минус |
|---|---|---|
| Реквизит объекта | данные до сотен килобайт, нужны вместе с объектом | утяжеляет каждое чтение |
| Отдельный регистр сведений | файлы и большие таблицы | лишний запрос при чтении |
| Файлы на диске или в томах | тысячи файлов, мегабайты каждый | нужны права и резервное копирование папки |
Ошибки при получении значения из хранилища и что с ними делать
Отдельно про реквизиты формы. Хранилище из объекта на управляемой форме недоступно на клиенте, поэтому даже правильный код в процедуре &НаКлиенте ничего не прочитает. Любая работа с ХранилищеЗначения — только в серверном контексте.
Частые вопросы
Чем хранилище значений отличается от хранилища конфигурации?
Можно ли положить в хранилище значений ссылку на документ?
Что вернёт Получить(), если в хранилище ничего не записывали?
Можно ли отобрать документы по содержимому хранилища запросом?
Как узнать, сколько места занимает хранилище?
Размер() у результата Получить(). Для других типов ориентир даёт длина строки ЗначениеВСтрокуВнутр(Хранилище): она пропорциональна размеру поля в базе.