Перенос реквизита типа ХранилищеЗначения между объектами, базами и файлами

· 9 мин

Содержание

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

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

Что лежит в ХранилищеЗначения и когда его приходится копировать

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

Что обычно кладут в такие реквизиты:

  • 🖼️ картинки: фото номенклатуры, сканы, логотипы для печатных форм;
  • 📎 файлы как ДвоичныеДанные: PDF, xlsx, вложения писем;
  • 🧩 служебные данные: структуры, таблицы значений, настройки отчётов, сохранённые параметры обработок;
  • 🔗 смешанное содержимое, где рядом с данными лежат ссылки на элементы справочников.

Типичные поводы для копирования:

  1. Нужно размножить элемент справочника вместе с картинкой или вложением.
  2. Данные переезжают из старой конфигурации в новую, и обмен не переносит нужный реквизит.
  3. Требуется выгрузить содержимое на диск: отдать файлы пользователю, разгрузить базу, сделать архив.
  4. Реквизит меняют на другой тип (например, файлы уходят в присоединённые файлы БСП), и старые данные надо переложить.

Не путайте с хранилищем конфигурации. Хранилище конфигурации — это отдельная база для групповой разработки, лежит в каталоге с файлом 1cv8ddb.1CD. Его копируют целиком папкой, когда к нему никто не подключён. Всё, что ниже, — про тип данных ХранилищеЗначения внутри информационной базы.

Способы копирования хранилища значений: какой выбрать

Выбор зависит от двух вопросов: куда переносите и есть ли внутри ссылки.

СпособКогда подходитРиски
Присваивание реквизита на сервереКопия внутри одной базыПочти нет; важно записать приёмник
Пересоздание через Получить() и Новый ХранилищеЗначенияНужно другое сжатие или правка содержимогоНагрузка на память при больших файлах
Выгрузка ДвоичныеДанные в файлКартинки и документы, перенос в любую базуТеряется всё, кроме самих байтов
Сериализация в XML (СериализаторXDTO)Перенос между базами с одинаковой структуройСсылки внутри не перекодируются
Выгрузка всей базы в .dt или копия через СУБДНужна полная копия со всеми хранилищамиТолько целиком, выборочно не получится
Прямое копирование поля средствами SQLНе рекомендуетсяВнутренний формат, обход платформы и её проверок

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

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

Работа с хранилищем идёт на сервере: там доступен объект справочника или документа, и там же нет лишней передачи больших данных на клиент.

Простая копия внутри базы

Пример для справочника с реквизитом Файл типа ХранилищеЗначения:

&НаСервереБезКонтекста
Процедура СкопироватьХранилищеНаСервере(Источник, Приемник)

	ОбъектПриемник = Приемник.ПолучитьОбъект();
	ОбъектПриемник.Файл = Источник.Файл;
	ОбъектПриемник.Записать();

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

Источник.Файл читается прямо через ссылку, отдельно получать объект-источник не нужно. Без вызова Записать() копия останется в памяти и пропадёт.

Копия с пересозданием и сжатием

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

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

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

Копия при интерактивном копировании объекта

Чтобы копия элемента всегда получала хранилище исходника, используйте обработчик ПриКопировании в модуле объекта:

Процедура ПриКопировании(ОбъектКопирования)

	Файл = ОбъектКопирования.Файл;

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

После правки проверьте сценарий вручную: скопируйте элемент, запишите и откройте вложение в новом элементе.

Массовое копирование из одного реквизита в другой

Частый случай — переложить данные из старого реквизита ФайлСтарый в новый ФайлНовый у всех элементов. Каждый объект пишется в своей транзакции, чтобы сбой на одном элементе не откатил всю пачку:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Т.Ссылка КАК Ссылка
|ИЗ
|	Справочник.Номенклатура КАК Т";

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

Объект.Записать() запускает все подписки и обработчики записи конфигурации. Если в них есть регистрация изменений для обмена или проверки, массовая запись создаст лишние сообщения обмена и может упасть на проверке заполнения. Для технической перекладки используйте Объект.ОбменДанными.Загрузка = Истина перед записью — это отключает обработчики и проверки, поэтому включайте его осознанно.

Выгрузка в файл и перенос между базами

Хранилище с файлом или картинкой

Если внутри лежат байты, их можно сохранить на диск и загрузить в другой базе. Сначала определите тип содержимого:

Значение = Источник.Файл.Получить();

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

Путь D:\Выгрузка\ указывается на компьютере, где выполняется код. В клиент-серверной базе это сервер 1С, и у пользователя, от которого запущена служба, должны быть права на запись в эту папку.

В базе-приёмнике обратная операция:

Данные = Новый ДвоичныеДанные("D:\Выгрузка\" + ИмяФайла);
Объект.Файл = Новый ХранилищеЗначения(Данные, Новый СжатиеДанных(9));
Объект.Записать();

Имя файла по уникальному идентификатору объекта удобно: при переносе обменом идентификаторы ссылок совпадают, и файл легко сопоставить с элементом.

Хранилище со структурой или таблицей

Структуры и таблицы значений переносят через XML. СериализаторXDTO умеет записывать ХранилищеЗначения как значение, а платформа упаковывает его содержимое в base64:

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

Чтение в другой базе — через ЧтениеXML и СериализаторXDTO.ПрочитатьXML(ЧтениеXML).

Ссылки внутри хранилища не перекодируются ни одним из способов. Если там лежит СправочникСсылка.Контрагенты, в чужой базе она найдёт элемент только при совпадении уникального идентификатора. Иначе после Получить() вы увидите «Объект не найден» с GUID в скобках. Такие хранилища перед переносом разбирайте и пересобирайте с поиском элементов по коду или ИНН.

Мини-кейс: перенос фото номенклатуры в новую базу

Торговая компания переходила на новую конфигурацию. Обмен перенёс 12 000 позиций номенклатуры, но фото в реквизите типа ХранилищеЗначения в правилах не было. Решение заняло один вечер:

  1. В старой базе обработка выгрузила каждое фото в файл с именем по уникальному идентификатору элемента.
  2. Папку скопировали на сервер новой базы.
  3. В новой базе обработка нашла элементы по идентификатору через Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(Имя)), проверила, что элемент существует, и записала фото.
  4. Элементы без файла и файлы без элемента попали в отдельный список для ручного разбора.

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

Копирование через файлы базы и SQL: где граница допустимого

Хранилища значений физически лежат в той же базе, что и всё остальное. В файловом варианте это 1Cv8.1CD, в клиент-серверном — таблицы СУБД.

Что можно делать на уровне файлов:

  • ✅ копировать каталог файловой базы целиком, когда все пользователи вышли;
  • ✅ делать резервную копию СУБД штатными средствами MS SQL Server или PostgreSQL;
  • ✅ выгружать базу в .dt через Конфигуратор: Администрирование → Выгрузить информационную базу.

Чего делать не стоит:

  • ❌ копировать файл 1Cv8.1CD во время работы пользователей: копия может оказаться несогласованной;
  • ❌ переносить поле с хранилищем между таблицами SQL-запросом. Формат поля внутренний, платформа не узнает о правке, а структура таблиц меняется от релиза к релизу;
  • ❌ пытаться вытащить одно хранилище из .dt — файл выгрузки загружается только целиком.

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

Автоматизация: обработка, регламентное задание, пакетный запуск

Разовую перекладку удобно оформить внешней обработкой: форма с выбором источника и приёмника и серверная процедура с кодом из раздела выше. Обработку открывают через Файл → Открыть в режиме «Предприятие».

Для регулярной выгрузки на диск подходят три варианта:

  1. Регламентное задание в доработанной конфигурации или расширении. Выполняется на сервере 1С по расписанию, удобно для ночной выгрузки вложений.
  2. Пакетный запуск с параметром /Execute, который открывает обработку сразу после входа:
"C:\Program Files\1cv8\8.3.24.1548\bin\1cv8.exe" ENTERPRISE /F "D:\Bases\Torg" /N "Робот" /P "пароль" /Execute "D:\Обработки\ВыгрузкаХранилищ.epf"

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

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

Для пакетного запуска заведите отдельного пользователя с правами только на нужные объекты. Пароль в bat-файле лежит открытым текстом, поэтому доступ к файлу ограничьте.

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

Проверка копии: размер, хеш, открытие

Запись без ошибки ещё не значит, что внутри то же самое. Проверка в три шага:

  1. Тип значения. ТипЗнч(Приемник.Файл.Получить()) совпадает с типом исходника.
  2. Размер. Для двоичных данных сравните Размер() у источника и приёмника.
  3. Контрольная сумма. Посчитайте хеш содержимого:
Функция ХешДанных(Данные)

	Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256);
	Хеширование.Добавить(Данные);
	Возврат ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма);

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

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

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

Перед тем как удалять исходный реквизит
Выполнено: 0 / 6

Резервное копирование данных из хранилищ

Отдельной «резервной копии хранилищ значений» в платформе нет: они входят в обычную копию базы . Поэтому правила общие.

  • 🗓️ Ежедневная копия СУБД или каталога файловой базы, раз в неделю дополнительно .dt.
  • 📦 Перед массовой перекладкой — внеочередная копия. Обработки массового копирования пишут сразу тысячи объектов, откатить их без копии нечем.
  • 🧪 Проверка восстановления: раз в месяц разверните копию в тестовую базу и откройте несколько вложений.

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

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