ХранилищеЗначения в 1С 8.3: чтение данных кодом, отладчиком и через двоичные данные

· 9 мин

Содержание

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

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

Что лежит внутри хранилища значений и откуда его читать

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

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

  • 📄 сканы договоров, PDF и другие файлы — как ДвоичныеДанные;
  • 🖼 фото товаров и подписи — тоже двоичные данные или объект Картинка;
  • 📊 таблицы значений и структуры — сохранённые настройки, кэш расчётов, параметры обмена;
  • 🔗 массивы ссылок на элементы справочников.

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

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

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

Способ 1. Посмотреть содержимое хранилища вручную через отладчик

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

  1. Откройте конфигуратор и запустите отладку через меню Отладка → Начать отладку. Для клиент-серверной базы на сервере 1С должна быть включена отладка (агент сервера запускают с ключом -debug).
  2. Найдите модуль, где объект уже прочитан на сервере, — например, обработчик ПриСозданииНаСервере формы документа. Поставьте точку останова клавишей F9.
  3. В пользовательском режиме откройте нужный документ. Выполнение остановится на точке.
  4. Нажмите Shift+F9 (вычислить выражение) и введите Объект.ИмяРеквизита.Получить(), где вместо ИмяРеквизита — имя реквизита-хранилища.
  5. Раскройте результат: у таблицы видны колонки и строки, у структуры — ключи, у двоичных данных — размер.

Не ставьте точки останова в рабочей базе в разгар дня — каждый пользователь, который откроет документ, будет ждать, пока вы отпустите отладчик. Работайте на копии базы.

Второй ручной вариант — внешняя обработка выполнения произвольного кода, которую администратор запускает с полными правами. В ней достаточно выполнить на сервере две строки:

Объект = Документы.ПоступлениеТоваровУслуг.НайтиПоНомеру("0000-000123", ТекущаяДата()).ПолучитьОбъект();
Сообщить(ТипЗнч(Объект.ИмяРеквизита.Получить()));

Имя документа и реквизита здесь — пример, подставьте свои. Поиск по номеру с датой работает, если нумерация периодическая; иначе ищите документ запросом.

Способ 2. Метод Получить() в коде конфигурации

Это основной путь — его используют в модулях форм, обработках и расширениях. Чтобы не тянуть в память весь объект, читайте поле запросом.

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

Справочник.Договоры и реквизит ДополнительныеДанные — условные имена. Функция возвращает значение уже распакованным. Если там таблица значений, на клиент её передать не получится — обработайте её на сервере и верните клиенту строку, число или массив.

Как записать значение, чтобы потом его прочитать

Без правильной записи читать нечего. Значение всегда оборачивают в конструктор:

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

Второй параметр — сжатие, уровень от 0 до 9. Для таблиц и текста оно заметно уменьшает объём, для JPEG и ZIP почти бесполезно: эти форматы уже сжаты.

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

Ресурс регистра сведений

Для регистра логика та же. Пример через менеджер записи:

МенеджерЗаписи = РегистрыСведений.ДанныеОбъектов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Объект = Ссылка;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
	Значение = МенеджерЗаписи.Данные.Получить();
КонецЕсли;

Имя регистра и измерения — снова пример. Метод Выбран() защищает от случая, когда записи по ключу нет.

Способ 3. Без файла: временное хранилище и строка внутреннего формата

Название «хранилище» в 1С носят три разные вещи, и их часто путают:

МеханизмГде живут данныеЧем читать
ХранилищеЗначенияв поле базы данных, постоянно.Получить()
Временное хранилищев памяти сеанса, пока жива форма или сеансПолучитьИзВременногоХранилища(Адрес)
Хранилища настроексистемные таблицы настроекХранилищеОбщихНастроек.Загрузить(...)

Временное хранилище — мост между сервером и клиентом. Типичный сценарий: достали значение из реквизита на сервере, положили во временное хранилище, а клиент забрал его по адресу.

&НаСервере
Функция АдресДанных(Ссылка)
	Значение = ДанныеИзХранилища(Ссылка);
	Возврат ПоместитьВоВременноеХранилище(Значение, УникальныйИдентификатор);
КонецФункции

&НаКлиенте
Процедура ПоказатьДанные(Команда)
	Адрес = АдресДанных(Объект.Ссылка);
	Данные = ПолучитьИзВременногоХранилища(Адрес);
КонецПроцедуры

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

Передать хранилище строкой

Иногда хранилище надо переслать целиком — в другую базу, в текстовое поле, в веб-сервис. Для этого есть строка внутреннего формата:

Строка = ЗначениеВСтрокуВнутр(Хранилище);
// ... передали строку ...
ХранилищеКопия = ЗначениеИзСтрокиВнутр(Строка);
Значение = ХранилищеКопия.Получить();

Обе функции работают на сервере и в толстом клиенте. Строка годится для обмена между базами 1С, но не для людей и не для чужих систем.

Хранилище в XML-файле обмена

Если вы разбираете выгрузку XML, хранилище там записано как строка Base64. Читайте его штатным сериализатором, а не вручную: СериализаторXDTO.ПрочитатьXML(ЧтениеXML) вернёт готовый объект ХранилищеЗначения, у которого затем вызывают Получить().

Способ 4. Двоичные данные: извлечь файл или картинку из хранилища

Самый частый практический случай — в реквизите лежит скан или фото, и его надо показать на форме или отдать пользователю файлом.

Показать картинку на форме

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

Обращение через точку Объект.Ссылка.Фото здесь допустимо для одного элемента. В цикле по сотням записей так не делайте — об этом ниже.

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

На сервере достаточно метода двоичных данных:

ДД = Хранилище.Получить();
Если ТипЗнч(ДД) = Тип("ДвоичныеДанные") Тогда
	ДД.Записать("D:\Выгрузка\scan_123.pdf");
КонецЕсли;

Путь здесь — путь на сервере 1С, а не на компьютере бухгалтера. Учётной записи службы сервера нужны права на запись в эту папку.

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

Перед записью узнайте размер: ДД.Размер() возвращает число байт. Если там 0 — файл сохранили пустым ещё при записи, искать ошибку надо в коде загрузки.

Способ 5. Проверка типа извлечённого значения

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

Функция ПрочитатьНастройки(Хранилище)
	
	Если ТипЗнч(Хранилище) <> Тип("ХранилищеЗначения") Тогда
		Возврат Неопределено; // NULL из левого соединения
	КонецЕсли;
	
	Значение = Хранилище.Получить();
	
	Если Значение = Неопределено Тогда
		Возврат Неопределено;
	ИначеЕсли ТипЗнч(Значение) = Тип("Структура") Тогда
		Если Значение.Свойство("Версия") И Значение.Версия = 2 Тогда
			Возврат Значение;
		КонецЕсли;
		Возврат ПреобразоватьСтаруюСтруктуру(Значение);
	ИначеЕсли ТипЗнч(Значение) = Тип("ТаблицаЗначений") Тогда
		Возврат Новый Структура("Версия, Таблица", 2, Значение);
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

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

Что ещё проверить после чтения:

  • ✅ у таблицы есть нужные колонки — Значение.Колонки.Найти("Сумма") <> Неопределено;
  • ✅ ссылки внутри не «битые» — объект мог быть удалён, а хранилище об этом не знает;
  • ✅ у двоичных данных размер больше нуля.

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

Большие хранилища значений: как не замедлить базу

Пока в хранилище лежат настройки на пару килобайт, о скорости думать не нужно. Проблемы начинаются со сканами по 5–10 МБ в реквизите справочника или документа.

Почему тормозит:

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

Что делать:

  1. Выносите тяжёлые данные в отдельный регистр сведений с измерением-владельцем. Документ остаётся лёгким, а файл читают только тогда, когда он нужен.
  2. Читайте запросом только нужное поле, а не объект целиком. В типовых конфигурациях на БСП для этого есть ОбщегоНазначения.ЗначениеРеквизитаОбъекта — он тоже работает через запрос.
  3. Включайте сжатие для текста и таблиц (Новый СжатиеДанных(9)). Для уже сжатых форматов уровень 0 сэкономит процессорное время.
  4. Не храните файлы в базе, если их много. Для десятков тысяч сканов надёжнее файловое хранилище на диске или в томах, а в базе — только ссылка.
ПодходКогда подходитМинус
Реквизит объектаданные до сотен килобайт, нужны вместе с объектомутяжеляет каждое чтение
Отдельный регистр сведенийфайлы и большие таблицылишний запрос при чтении
Файлы на диске или в томахтысячи файлов, мегабайты каждыйнужны права и резервное копирование папки

Ошибки при получении значения из хранилища и что с ними делать

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

Отдельно про реквизиты формы. Хранилище из объекта на управляемой форме недоступно на клиенте, поэтому даже правильный код в процедуре &НаКлиенте ничего не прочитает. Любая работа с ХранилищеЗначения — только в серверном контексте.

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

Чем хранилище значений отличается от хранилища конфигурации?
Это разные механизмы. Хранилище конфигурации — система групповой разработки в конфигураторе. ХранилищеЗначения — тип данных для реквизита, куда упаковывают значение.
Можно ли положить в хранилище значений ссылку на документ?
Можно. Но платформа не отслеживает такую ссылку: поиск ссылок её не найдёт, а удаление помеченных объектов не предупредит о связи.
Что вернёт Получить(), если в хранилище ничего не записывали?
Неопределено. Ошибки не будет, поэтому проверку на пустое значение нужно делать самому.
Можно ли отобрать документы по содержимому хранилища запросом?
Нет. Поле хранилища нельзя использовать в условиях и сортировке. Если по признаку нужно отбирать, храните его в отдельном реквизите обычного типа.
Как узнать, сколько места занимает хранилище?
Для двоичных данных — Размер() у результата Получить(). Для других типов ориентир даёт длина строки ЗначениеВСтрокуВнутр(Хранилище): она пропорциональна размеру поля в базе.