К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как записать файл в хранилище значений 1С

Как записать файл в хранилище значений 1С: работа с типом ХранилищеЗначения, запись и чтение файлов, сжатие, примеры кода на встроенном языке 1С.

Хранилище значения — специальный тип данных в платформе 1С, предназначенный для сохранения произвольных данных, включая двоичное содержимое файлов, картинки, сериализованные объекты и произвольные строки большого объёма. Типичный сценарий: в справочнике «Номенклатура» есть реквизит типа ХранилищеЗначения, в который нужно поместить PDF-файл спецификации или изображение товара, чтобы файл хранился вместе с базой и был доступен всем пользователям.

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

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

Что такое хранилище значения и когда его применять для файлов

Тип ХранилищеЗначения — это обёртка, которая позволяет сохранить в реквизите объекта конфигурации данные любого сериализуемого типа: двоичные данные, картинку, таблицу значений, структуру, массив. Физически содержимое хранится в поле таблицы базы данных как BLOB, поэтому размер одного значения практически ограничен лишь настройками СУБД и здравым смыслом.

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

Подготовка: реквизит объекта и права доступа

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

Для записи пользователю нужны права на изменение объекта. Если запись выполняется из фонового задания или внешней обработки, проверьте, что код исполняется на сервере: конструктор ДвоичныеДанные по пути к файлу работает с файловой системой того компьютера, где выполняется код. На клиенте в веб-клиенте прямой путь к файлу недоступен — там используется помещение файла во временное хранилище.

Базовый код записи файла в хранилище значений

Минимальный сценарий записи файла с диска в реквизит объекта выглядит так: создаём объект ДвоичныеДанные по пути, оборачиваем в хранилище, присваиваем, записываем объект.

// Серверная процедура

Процедура ЗаписатьФайлВХранилище(СсылкаНаОбъект, ПутьКФайлу) Экспорт

Объект = СсылкаНаОбъект.ПолучитьОбъект();

ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);

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

Объект.ИмяФайла = "Спецификация.pdf";

Объект.РазмерФайла = ДвоичныеДанные.Размер();

Объект.Записать();

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

Второй параметр конструктора — объект СжатиеДанных со степенью сжатия от 0 (без сжатия) до 9 (максимальное). Для текстовых файлов, XML и несжатых изображений сжатие даёт заметную экономию места; для уже сжатых форматов (JPG, ZIP, большинство PDF) выигрыш минимален, а время записи немного растёт. Если сжатие не нужно, параметр можно опустить: Новый ХранилищеЗначения(ДвоичныеДанные).

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

bsl
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Files\spec.pdf");
Объект.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.Записать();

Запись файла из тонкого и веб-клиента через временное хранилище

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

// Клиент

&НаКлиенте

Процедура ЗагрузитьФайл(Команда)

НачатьПомещениеФайла(

Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект),

, , Истина, УникальныйИдентификатор);

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

&НаКлиенте

Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, Доп) Экспорт

Если Результат Тогда

ЗаписатьНаСервере(Адрес, ВыбранноеИмяФайла);

КонецЕсли;

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

// Сервер

&НаСервере

Процедура ЗаписатьНаСервере(Адрес, ИмяФайла)

ДвоичныеДанные = ПолучитьИзВременногоХранилища(Адрес);

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

Объект.ИмяФайла = ИмяФайла;

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

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

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

Чтение файла из хранилища и сохранение на диск

Обратная операция выполняется методом Получить(), который возвращает значение, помещённое при записи. Если туда клали ДвоичныеДанные — вернутся ДвоичныеДанные, у которых есть метод Записать(ИмяФайла) для сохранения на диск.

ДвоичныеДанные = Объект.ДанныеФайла.Получить();

Если ДвоичныеДанные = Неопределено Тогда

ВызватьИсключение "Файл в хранилище отсутствует";

КонецЕсли;

ДвоичныеДанные.Записать("C:\Temp\" + Объект.ИмяФайла);

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

Контрольные точки

Как проверить результат

Размер записанных данных
Норма

РазмерФайла совпадает с размером исходного файла на диске

Тревожный признак

Размер нулевой или Получить() возвращает Неопределено

Открытие выгруженного файла
Норма

Файл открывается в штатной программе без ошибок

Тревожный признак

Файл повреждён или не открывается — вероятно, записан не тот тип данных

Имя файла при выгрузке
Норма

Имя и расширение совпадают с исходными

Тревожный признак

Имя потеряно — не сохранён реквизит с именем

Сравнение подходов: хранилище значения или присоединённые файлы

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

КритерийХранилище значенияПрисоединённые файлы БСП
Место храненияВ таблице объекта в базеВ базе или в томах на диске
ЗависимостиТолько платформаТребуется БСП
Метаданные файлаНужны отдельные реквизитыИмя, размер, автор хранятся штатно
Массовое хранение скановНе рекомендуетсяРекомендуется
Влияние на размер базыПрямое, файл в базеМожно вынести в тома

Сравнение вариантов

Хранилище значения в реквизите

Подходит для единичных небольших файлов: подписи, шаблоны, логотипы. Ограничение — рост размера базы и отсутствие штатного интерфейса.

Присоединённые файлы БСП

Подходит для массового хранения документов и изображений, есть тома, версии и готовый интерфейс. Ограничение — зависимость от библиотеки.

Внешний каталог + путь в реквизите

Файлы лежат на сетевом ресурсе, в базе только путь. Быстро и легко для базы, но требуется контроль доступа и целостности ссылок.

Типичные ошибки и ограничения

Самая частая ошибка — попытка прочитать файл напрямую на сервере по пути, существующему только на клиенте. Второе место занимает запись в хранилище не двоичных данных, а строки с путём к файлу: Новый ХранилищеЗначения("C:\file.pdf") сохранит строку пути, а не содержимое файла, и при чтении вы получите текст вместо данных.

Обратите внимание на ограничения: большие файлы (десятки и сотни мегабайт) в реквизите объекта замедляют выборки, если реквизит попадает в запросы — в СУБД такие поля читаются вместе со строкой таблицы. В файловом варианте базы объём данных в одном хранилище дополнительно ограничен возможностями файловой СУБД. Также помните, что при обмене данными (РИБ, планы обмена) хранилища значений передаются целиком, что увеличивает трафик.

Типичные ошибки
01
Записать в хранилище строку с путём вместо ДвоичныеДанные — при чтении получите текст пути, а не файл
02
Читать файл с диска в серверном коде по клиентскому пути — сервер не видит диск пользователя, используйте временное хранилище
03
Не сохранить имя и расширение файла — при выгрузке файл получит безымянное имя и не откроется по ассоциации
04
Хранить сотни мегабайт в реквизите объекта — раздувание базы, медленные выборки и бэкапы
05
Не проверить Получить() на Неопределено — ошибка исполнения при обращении к пустому хранилищу

Проверка результата и обслуживание

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

Периодически полезно искать «осиротевшие» данные: объекты, где реквизит-хранилище заполнен, но сам объект помечен на удаление, или хранилища с устаревшими версиями файлов. Простой запрос с проверкой НЕ ДанныеФайла ЕСТЬ NULL и размером помогает оценить, сколько места занимают вложения, и принять решение о чистке или миграции в тома.

Итог

Что важно запомнить

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

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

Можно ли хранить в хранилище значения не файл, а таблицу значений или структуру?

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

Какой максимальный размер файла можно записать в хранилище значений?

Жёсткого лимита в платформе нет — ограничения задают СУБД и разумная эксплуатация. На практике файлы до нескольких мегабайт работают без проблем; десятки и сотни мегабайт уже ударяют по размеру базы, скорости выборок и обменов. Для крупных файлов используйте тома присоединённых файлов.

Почему после записи Получить() возвращает Неопределено?

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

Нужно ли указывать сжатие при записи файла?

Не обязательно. Сжатие полезно для текстовых и несжатых данных (XML, TXT, BMP), где экономия может быть значительной. Для уже сжатых форматов (JPG, ZIP, большинство PDF) выигрыш незначителен, поэтому многие разработчики либо не указывают сжатие, либо применяют его выборочно по расширению файла.

Как выгрузить файл из хранилища пользователю через веб-клиент?

На сервере получите ДвоичныеДанные методом Получить(), поместите их во временное хранилище через ПоместитьВоВременноеХранилище, а на клиенте вызовите ПолучитьФайл(Адрес, ИмяФайла, Истина). Пользователь увидит диалог сохранения, и файл скачается через браузер без доступа к файловой системе сервера.