Хранилище значений в 1С 8.3: как упаковать таблицу, файл или картинку в реквизит

· 9 мин

Содержание

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

Тип общий для платформы и от конфигурации не зависит. Код ниже одинаково работает в БП 3.0, ЗУП 3.1, УТ 11 и в самописных базах. Хранилище доступно на сервере, в толстом клиенте и во внешнем соединении. На тонком клиенте объекта нет, поэтому все операции с ним выносят в серверные процедуры.

Что хранит тип ХранилищеЗначения и где его применяют

Реквизит справочника или документа принимает ограниченный набор типов: строку, число, дату, булево, ссылки. Таблицу значений или PDF-файл в такой реквизит напрямую не положить. Хранилище значений закрывает этот пробел: платформа сериализует значение во внутренний формат и пишет в базу как двоичный блок.

Где это встречается на практике:

  • 🖼️ фото сотрудника или товара в карточке справочника;
  • 📎 сканы договоров и других документов, приложенные к объекту;
  • 📊 сохранённая таблица расчёта, чтобы потом показать, «из чего сложилась сумма»;
  • ⚙️ настройки обработки или обмена в константе: структура с десятком параметров;
  • 🧾 готовый табличный документ печатной формы, сохранённый на момент печати.

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

Какие значения поместить нельзя

В хранилище попадает только то, что поддерживает сериализацию. Объекты-инструменты (например, Запрос, ЧтениеXML, ЗаписьXML) и формы туда не помещаются: на конструкторе возникнет исключение. Если сомневаетесь, проверьте значение заранее через ЗначениеВСтрокуВнутр() в попытке.

Как создать хранилище значений: конструктор и сжатие

Конструктор принимает два параметра:

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

Без второго параметра данные пишутся без сжатия. Уровень 9 даёт наименьший объём, но тратит больше процессора на запись. Для большинства задач пишут так:

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

Когда сжатие помогает, а когда нет

  • ✅ Таблицы значений, структуры, текст, XML, JSON — сжимаются в разы, выигрыш заметный.
  • ❌ JPEG, PNG, PDF со сканами, ZIP — уже сжаты. Повторное сжатие почти ничего не даёт, только тратит время.

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

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

Запись и чтение данных в реквизите хранилища значений

Главное правило: в реквизит кладут не само значение, а объект хранилища. Присваивание СправочникОбъект.ДанныеРасчета = ТаблицаРасчета либо завершится ошибкой, либо не сохранит то, что вы ожидаете.

Запись в объект

&НаСервере
Процедура СохранитьРасчет(Ссылка, ТаблицаРасчета)

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

КонецПроцедуры

Для константы:

Настройки = Новый Структура("Каталог, Период", "D:\Обмен", 30);
Константы.НастройкиОбмена.Установить(Новый ХранилищеЗначения(Настройки));

Запись в регистр сведений

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

&НаСервере
Процедура ЗаписатьФайлВладельца(Владелец, ДанныеФайла)

    МенеджерЗаписи = РегистрыСведений.ФайлыДокументов.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Владелец = Владелец;
    МенеджерЗаписи.Файл = Новый ХранилищеЗначения(ДанныеФайла);
    МенеджерЗаписи.Записать(Истина);

КонецПроцедуры

Параметр Истина в Записать() (он же значение по умолчанию) замещает существующую запись с тем же владельцем. С Ложь повторная запись по тому же измерению завершится ошибкой о дублировании записи, поэтому указывайте параметр явно — так код читается однозначно.

Чтение

ТаблицаРасчета = СправочникОбъект.ДанныеРасчета.Получить();
Если ТаблицаРасчета = Неопределено Тогда
    // в реквизит ещё ничего не записывали
КонецЕсли;

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

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

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

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

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

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

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

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

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

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

КонецПроцедуры

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)

    Если ЭтоАдресВременногоХранилища(АдресФото) Тогда
        ТекущийОбъект.Фото = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(АдресФото));
    КонецЕсли;

КонецПроцедуры

ТекущийОбъект в ПередЗаписьюНаСервере — это уже настоящий объект базы, и у него реквизит Фото доступен. Новый файл с диска пользователя загружают на сервер стандартными методами помещения файла во временное хранилище; полученный адрес записывают в АдресФото, и поле картинки сразу показывает изображение.

Передавайте в ПоместитьВоВременноеХранилище уникальный идентификатор формы. Тогда данные живут, пока открыта форма, и не висят в памяти сервера до конца сеанса.

Чем ХранилищеЗначения отличается от других типов данных

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

СпособГде живут данныеКогда выбиратьОграничения
ХранилищеЗначения в реквизитев базе, постояннофайлы, таблицы, настройки объектанет поиска и отбора по содержимому
Строка неограниченной длиныв базе, постояннотекст, JSON, XML, который нужно читать глазамитолько текст; отбор и сравнение ограничены
ДвоичныеДанныев памятипромежуточная работа с файломв реквизит напрямую не записать, только через хранилище
Временное хранилищена сервере, до конца сеанса или жизни формыпередача данных между клиентом и серверомв базу не попадает
Хранилища настроек (ХранилищеОбщихНастроек)в базе, по пользователюпользовательские настройкиключи объекта и настройки, а не данные документа
Табличная частьв базе, постоянноданные, по которым нужны отчётыструктура задаётся в Конфигураторе

Практический ориентир: если по данным потом строят отчёт или ставят отбор — это табличная часть или регистр. Хранилище подходит для «чёрного ящика», который целиком сохранили и целиком достали.

Ссылки внутри сохранённой таблицы хранятся как ссылки. Если элемент справочника потом удалили, при чтении в таблице окажется битая ссылка «Объект не найден». Контроль ссылочной целостности на содержимое хранилища не распространяется.

Сериализация хранилища и работа с файлами на диске

Положить файл в хранилище и выгрузить обратно

&НаСервере
Процедура ПриложитьСкан(СправочникОбъект, ПутьКФайлу)

    ДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
    СправочникОбъект.Скан = Новый ХранилищеЗначения(ДанныеФайла);
    СправочникОбъект.ИмяФайлаСкана = "договор.pdf";

КонецПроцедуры

&НаСервере
Процедура ВыгрузитьСкан(Ссылка, КаталогВыгрузки)

    ДанныеФайла = Ссылка.Скан.Получить();
    Если ТипЗнч(ДанныеФайла) = Тип("ДвоичныеДанные") Тогда
        ДанныеФайла.Записать(КаталогВыгрузки + "\" + Ссылка.ИмяФайлаСкана);
    КонецЕсли;

КонецПроцедуры

Храните имя и расширение файла в отдельном строковом реквизите. Хранилище помнит только содержимое, и без имени потом неясно, PDF там или DOCX.

Код на сервере работает с файловой системой сервера 1С, а не компьютера пользователя. Путь C:\Сканы\ в клиент-серверной базе ищется на сервере. Файлы пользователя передают через временное хранилище.

Сериализация в строку и XML

Хранилище можно превратить в строку внутреннего формата и обратно:

СтрокаХранилища = ЗначениеВСтрокуВнутр(Хранилище);
Хранилище = ЗначениеИзСтрокиВнутр(СтрокаХранилища);

Для обмена между базами используют XDTO-сериализацию. Хранилище записывается в XML как двоичный блок в Base64:

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("D:\Обмен\настройки.xml");
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Хранилище);
ЗаписьXML.Закрыть();

Обратное чтение — через ЧтениеXML и СериализаторXDTO.ПрочитатьXML(). Если внутри лежат ссылки, база-приёмник должна знать эти объекты, иначе после загрузки получите те же битые ссылки.

Для обмена с внешними системами (сайт, мобильное приложение) внутренний формат 1С не годится. Достаньте значение через Получить() и сериализуйте его сами: двоичные данные — в Base64 через Base64Строка(), структуру — в JSON.

Производительность и частые ошибки с хранилищем значений

Как не замедлить базу

Каждый мегабайт в хранилище — это мегабайт в таблице базы и в каждой резервной копии. Пара практических правил:

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

Разбор типичных ошибок

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

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

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