Разработка и Конфигурация · 11 мин
Как сохранить файл в хранилище значений 1С
Как сохранить файл в хранилище значений 1С: работа с ХранилищеЗначения, ДвоичныеДанные, ПолучитьИзВременногоХранилища. Примеры кода, ошибки, FAQ.
Хранилище значений (ХранилищеЗначения) — штатный механизм платформы 1С, который позволяет поместить файл прямо в базу данных: в реквизит справочника, документа или регистра сведений. Такой подход используют для хранения изображений номенклатуры, сканов договоров, печатей и подписей, вложений писем. Файл упаковывается в двоичные данные, сжимается и записывается в поле специального типа, после чего доступен на любом клиенте без доступа к файловой системе сервера.
Типовая цепочка выглядит так: файл читается с диска или из временного хранилища как ДвоичныеДанные, оборачивается конструктором Новый ХранилищеЗначения() и присваивается реквизиту объекта с типом «ХранилищеЗначения». Обратная операция — метод Получить() — возвращает исходные двоичные данные, которые можно записать на диск или показать пользователю.
Ниже разобрана полная процедура: от подготовки реквизита в конфигураторе до кода записи и чтения на клиенте и сервере, а также ограничения, типичные ошибки и альтернативы для крупных файлов.
Подготовка реквизита типа «ХранилищеЗначения» в конфигураторе
Прежде чем писать код, нужно место для хранения. Откройте конфигуратор, найдите нужный объект (например, справочник «Номенклатура» или регистр сведений «ПрисоединенныеФайлы») и добавьте реквизит с типом «ХранилищеЗначения». Этот тип не имеет длины и не индексируется — платформа хранит содержимое в служебном формате, часто в отдельной таблице СУБД.
Для регистров сведений хранилище обычно делают ресурсом, а не измерением: измерения участвуют в уникальности записи, и помещать туда бинарные данные бессмысленно. Для справочников и документов реквизит добавляется как обычное поле объекта.
Рекомендуется сразу добавить сопутствующие реквизиты: имя файла (строка), расширение, размер, дата загрузки. Само хранилище этих сведений не содержит, и без них вы не сможете показать пользователю список файлов или восстановить исходное имя при выгрузке.
Запись файла в хранилище: серверный код
Базовый сценарий — файл уже лежит на диске сервера или пришёл из клиента через временное хранилище. Серверный код читает его в ДвоичныеДанные и сохраняет:
Функция СохранитьФайлВХранилище(СсылкаНаОбъект, АдресВоВременномХранилище, ИмяФайла) Экспорт
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВоВременномХранилище);
Объект = СсылкаНаОбъект.ПолучитьОбъект();
Объект.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.ИмяФайла = ИмяФайла;
Объект.РазмерФайла = ДвоичныеДанные.Размер();
Объект.Записать();
УдалитьИзВременногоХранилища(АдресВоВременномХранилище);
КонецФункции
Конструктор Новый ХранилищеЗначения принимает два параметра: сами данные и необязательный объект СжатиеДанных. Сжатие со степенью 9 уменьшает объём текстовых и офисных файлов, но почти бесполезно для jpeg, mp4 и zip — они уже сжаты. При чтении распаковка выполняется автоматически, указывать параметры сжатия повторно не нужно.
Если файл лежит на диске сервера, вместо временного хранилища используйте ДвоичныеДанные = Новый ДвоичныеДанные("C:\Files\dogovor.pdf"). Учтите, что путь должен существовать именно на сервере 1С, а не на компьютере пользователя — частая причина ошибки «файл не найден».
- 1Добавьте реквизит типа «ХранилищеЗначения» и обновите конфигурацию БД
- 2На клиенте вызовите НачатьПомещениеФайлаВХранилище или ПоместитьФайл для получения адреса временного хранилища
- 3В серверной процедуре получите ДвоичныеДанные через ПолучитьИзВременногоХранилища
- 4Присвойте реквизиту Новый ХранилищеЗначения(ДвоичныеДанные)
- 5Вызовите Записать() и удалите данные из временного хранилища
- 6Проверьте: реквизит заполнен, метод Получить() возвращает данные того же размера
Передача файла с клиента на сервер
В управляемом приложении клиент не имеет прямого доступа к серверным объектам, поэтому файл сначала помещается во временное хранилище. В асинхронной модели (тонкий и веб-клиент) используется НачатьПомещениеФайлаВХранилище с описанием оповещения:
&НаКлиенте
Процедура ВыбратьФайл(Команда)
Описание = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайлаВХранилище(Описание, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДопПараметры) Экспорт
Если Результат Тогда
СохранитьФайлНаСервере(Адрес, ВыбранноеИмяФайла);
КонецЕсли;
КонецПроцедуры
Временное хранилище живёт в рамках сеанса или до ближайшего серверного вызова в зависимости от способа помещения. Если между помещением файла и его сохранением проходит много серверных вызовов, данные могут быть удалены — передавайте адрес и сохраняйте файл в одном логическом действии. После записи вызывайте УдалитьИзВременногоХранилища, чтобы не копить данные в памяти сервера.
Чтение файла из хранилища и выгрузка на диск
Обратная операция выполняется методом Получить(). Он возвращает значение, помещённое в хранилище, — в нашем случае ДвоичныеДанные:
&НаСервере
Функция ПолучитьФайлИзХранилища(СсылкаНаОбъект) Экспорт
ДвоичныеДанные = СсылкаНаОбъект.ДанныеФайла.Получить();
Если ДвоичныеДанные = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанные, Новый УникальныйИдентификатор);
КонецФункции
На клиенте адрес из временного хранилища передаётся в метод ПолучитьФайл или НачатьПолучениеФайлаИзХранилища, который сохраняет данные по указанному пользователем пути. Если нужно просто показать картинку в форме, достаточно присвоить адрес временного хранилища реквизиту формы с видом «Поле картинки» — запись на диск не требуется.
Всегда проверяйте результат Получить() на Неопределено: пустое хранилище не вызывает исключения, а возвращает именно его. Попытка вызвать методы ДвоичныеДанные у Неопределено — одна из самых частых ошибок времени выполнения в этом сценарии.
Хранилище значений против томов и внешних хранилищ
Хранилище значений — не единственный способ прикрепить файл в 1С. Выбор зависит от объёма данных, частоты доступа и требований к резервному копированию.
Сравнение вариантов хранения
Подходит для небольших файлов: картинки товаров, сканы, печати. Всё внутри базы, простой код, но рост размера БД и бэкапов.
Файлы лежат в каталоге на диске, в базе только ссылки. Подходит для больших объёмов; требует отдельного резервного копирования каталога.
Отвязывает файлы от объекта-владельца, позволяет хранить несколько файлов и версии. Чуть сложнее код, гибче структура.
Для очень больших объёмов и распределённых систем. Требует интеграции и не входит в стандартный бэкап 1С.
Практическое правило: если совокупный объём файлов исчисляется мегабайтами или единицами гигабайт — хранилище значений удобно и безопасно. Если речь о десятках гигабайт вложений — выносите файлы в тома, иначе регламентные операции с базой станут заметно тяжелее.
Проверка результата и отладка
После записи убедитесь, что данные действительно сохранились. Самый быстрый способ — консоль кода или отладчик: выполните Получить() и сравните размер полученных данных с размером исходного файла. Совпадение размера и успешная запись на диск с корректным открытием файла — достаточное подтверждение.
Если файл после выгрузки «битый», проверьте три места: не записали ли вы в хранилище строку с путём вместо двоичных данных; не применялось ли двойное кодирование при передаче через веб-сервисы (Base64 поверх Base64); не потерялось ли расширение, из-за чего программа-просмотрщик не распознаёт формат.
Как проверить результат
Возвращает ДвоичныеДанные, размер совпадает с исходным файлом
Возвращает Неопределено или данные другого типа
Файл открывается штатной программой без ошибок
Файл не открывается, нулевой размер или неверное расширение
Заполнены и соответствуют загруженному файлу
Пустые — при выгрузке имя файла восстановить нельзя
Ограничения и безопасная эксплуатация
Хранилище значений сериализует данные во внутренний формат платформы, поэтому читать его напрямую средствами СУБД бессмысленно — только через платформу. Это же означает, что перенос данных между информационными базами выполняется штатно: выгрузка в dt, обмены и конвертация данных корректно переносят содержимое хранилищ.
При массовой загрузке файлов обрабатывайте их порциями в транзакциях разумного размера. Запись тысяч объектов с бинарными данными в одной транзакции может привести к росту журнала транзакций СУБД и блокировкам. Для первоначального переноса архивов файлов используйте фоновые задания с контролем ошибок по каждому элементу.
Учитывайте права доступа: реквизит с хранилищем подчиняется стандартным правам на объект. Если файл содержит персональные данные или коммерческую тайну, ограничьте доступ на уровне ролей, а при необходимости — шифруйте данные перед помещением в хранилище средствами платформы или внешних компонент.
Что важно запомнить
Файл сохраняется в хранилище значений тремя шагами: получить ДвоичныеДанные, обернуть их в Новый ХранилищеЗначения и записать объект. Чтение — метод Получить() с обязательной проверкой на Неопределено. Храните рядом имя, расширение и размер файла, а для больших объёмов выбирайте тома хранения вместо записи в базу.
Частые вопросы
Можно ли хранить в хранилище значений не файл, а другие данные?
Да. Хранилище принимает практически любые сериализуемые значения: строки, таблицы значений, структуры, картинки. Часто в нём сохраняют настройки отчётов и произвольные параметры. Ограничение — значение должно поддерживать сериализацию платформой; COM-объекты и ссылки на объекты других баз сохранить нельзя.
Как узнать размер файла, уже записанного в хранилище?
Вызовите Получить() и затем метод Размер() у полученных ДвоичныхДанных. Отдельного способа узнать размер без извлечения данных платформа не предоставляет, поэтому при загрузке размер лучше сохранять в отдельный числовой реквизит — это избавит от лишних чтений при выводе списков файлов.
Почему после записи реквизит остаётся пустым?
Чаще всего забыт вызов Записать() после присвоения реквизита, либо запись выполняется в другой транзакции, которая откатывается из-за ошибки дальше по коду. Также проверьте, что работаете с объектом, полученным через ПолучитьОбъект(), а не пытаетесь присвоить значение реквизиту ссылки напрямую — у ссылки реквизиты доступны только для чтения.
Нужно ли указывать сжатие при чтении из хранилища?
Нет. Параметры сжатия задаются один раз при создании хранилища и сохраняются вместе с данными. Метод Получить() автоматически возвращает данные в исходном виде, независимо от того, применялось ли сжатие при записи.
Что делать, если файлов много и база сильно выросла?
Рассмотрите перенос вложений в тома хранения файлов (подсистема «Работа с файлами» БСП) или во внешнее хранилище. Для уже накопленных данных напишите обработку переноса: чтение из хранилища, запись файла в том, очистка реквизита — с выполнением порциями в фоновом задании и обязательной резервной копией перед стартом.
Можно ли записать файл в хранилище через COM-соединение из другой базы?
Да. Через COM-коннектор доступны те же типы: создайте ДвоичныеДанные через Новый COMОбъект с вызовом NewObject("ДвоичныеДанные", путь) в контексте соединения, затем присвойте реквизиту объект NewObject("ХранилищеЗначения", данные). Учитывайте, что передача больших файлов через COM медленнее прямой загрузки.