Разработка и Конфигурация · 10 мин
Как поместить файл в хранилище значений 1С
Разбираем, как поместить файл в хранилище значений 1С: код помещения и извлечения, работа с реквизитами, ограничения, типовые ошибки и проверка результата.
Хранилище значений — встроенный механизм платформы 1С, который позволяет сохранять в базе данных произвольные данные: файлы, картинки, сериализованные объекты, таблицы значений. Чаще всего его используют для хранения вложений в справочниках и документах: сканов договоров, фотографий товаров, печатных форм в формате PDF.
Механизм работает одинаково на всех актуальных версиях платформы 8.3: файл сначала считывается в объект типа ДвоичныеДанные, затем упаковывается через конструктор Новый ХранилищеЗначения() и записывается в реквизит объекта с типом «ХранилищеЗначения». Обратная операция — метод Получить() — возвращает исходные двоичные данные.
Ниже разобран полный цикл: подготовка реквизита в конфигураторе, помещение файла из клиентского приложения и с сервера, извлечение и сохранение на диск, а также ограничения, о которых нужно знать до того, как хранить в базе большие файлы.
Подготовка: создание реквизита с типом «ХранилищеЗначения»
Прежде чем помещать файл, в конфигурации должен существовать реквизит, способный его принять. Откройте конфигуратор, найдите нужный объект (справочник, документ или создайте отдельный регистр сведений для вложений) и добавьте реквизит с типом «ХранилищеЗначения». Например, для справочника «Номенклатура» это может быть реквизит «ФайлКартинки».
Альтернатива — хранение файлов в томах на диске через механизм «Присоединенные файлы» из библиотеки стандартных подсистем (БСП). Если конфигурация построена на БСП, часто правильнее использовать штатную подсистему «Работа с файлами», а не писать собственный код. Хранилище значений оправдано, когда файлов немного, они небольшие и нужна максимально простая схема без дополнительных справочников.
Где хранить файл
Подходит для небольших файлов и простых решений; минус — рост размера базы и невозможность частичного чтения
Стандарт для типовых конфигураций, поддержка версий и томов; требует подключенной подсистемы
Подходит для больших архивов; минус — файл не защищён резервным копированием базы и правами 1С
Базовый код: помещение файла в хранилище значений
Ключевые объекты — ДвоичныеДанные и ХранилищеЗначения. Двоичные данные читают файл с диска, хранилище упаковывает их для записи в базу. Классический серверный вариант выглядит так:
&НаСервере
Процедура ПоместитьФайлВХранилище(СсылкаНаОбъект, ПутьКФайлу)
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
Объект = СсылкаНаОбъект.ПолучитьОбъект();
Объект.ФайлДанные = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.Записать();
КонецПроцедуры
Параметр Новый СжатиеДанных(9) включает максимальное сжатие. Для уже сжатых форматов (JPG, ZIP, PDF с картинками) выигрыш минимален, а для XML, текста и BMP размер может уменьшиться в разы. Сжатие не меняет логику извлечения — метод Получить() сам вернёт распакованные данные.
Обратите внимание: запись выполняется через ПолучитьОбъект(), а не через установку значения реквизита формы. Изменение реквизита формы сохранится только после записи объекта, а при работе «напрямую» через ссылку объект нужно явно записать.
Помещение файла из клиентского приложения
В тонком и веб-клиенте прямой доступ к файловой системе с сервера невозможен — файл сначала передаётся на сервер через временное хранилище. Стандартная схема: на клиенте вызывается НачатьПомещениеФайла, файл попадает во временное хранилище, а серверная процедура забирает его оттуда по адресу.
&НаКлиенте
Процедура ВыбратьФайл(Команда)
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайла(ОписаниеОповещения, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДопПараметры) Экспорт
Если Результат Тогда
СохранитьФайлНаСервере(Адрес);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура СохранитьФайлНаСервере(Адрес)
ДвоичныеДанные = ПолучитьИзВременногоХранилища(Адрес);
Объект = РеквизитФормыВЗначение("Объект");
Объект.ФайлДанные = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
Объект.Записать();
КонецПроцедуры
Важный нюанс: ПолучитьИзВременногоХранилища возвращает именно двоичные данные, если файл помещался стандартным способом. Если же вы помещали туда что-то иное, проверьте тип возвращаемого значения перед упаковкой.
Извлечение файла и сохранение на диск
Обратная операция проста: метод Получить() у значения реквизита возвращает ровно те двоичные данные, которые были помещены, с учётом автоматической распаковки сжатия.
&НаСервере
Функция ПолучитьФайлИзХранилища(СсылкаНаОбъект) Экспорт
ДвоичныеДанные = СсылкаНаОбъект.ФайлДанные.Получить();
Если ДвоичныеДанные = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанные);
КонецФункции
На клиенте данные сохраняются через ПолучитьФайл или, в толстом клиенте, методом Записать(Путь) у двоичных данных. Обязательно проверяйте результат на Неопределено: пустое хранилище возвращает именно его, и попытка записать «пустоту» в файл приведёт к ошибке.
Если имя и расширение исходного файла важны (например, чтобы открыть его ассоциированной программой), храните их в отдельных строковых реквизитах рядом с хранилищем — само хранилище значений имя файла не сохраняет.
Ограничения и влияние на базу данных
Каждый файл, помещённый в хранилище значений, физически увеличивает размер информационной базы. В файловом варианте таблица с большими вложениями быстро раздувает файл 1Cv8.1CD, что замедляет резервное копирование, тестирование и обновление. В клиент-серверном варианте растут таблицы СУБД и журналы транзакций.
Практические рекомендации: не храните в хранилище значений файлы размером в десятки и сотни мегабайт, используйте сжатие для текстовых и XML-данных, а при массовой загрузке вложений рассмотрите тома хранения файлов БСП. Также учтите, что реквизит типа «ХранилищеЗначения» нельзя индексировать и использовать в отборах запроса осмысленным образом — поиск по содержимому файлов невозможен.
Типичные ошибки и их диагностика
Самая частая проблема — попытка записать в реквизит двоичные данные напрямую, без обёртки Новый ХранилищеЗначения(). Платформа выдаст ошибку несоответствия типов. Вторая по частоте — чтение Получить() без проверки на Неопределено для объектов, где файл ещё не загружали.
Ещё одна группа ошибок связана с клиент-серверным взаимодействием: вызов Новый ДвоичныеДанные(Путь) на сервере с путём, который существует только на компьютере пользователя. Сервер не видит диски клиента — файл нужно сначала передать через временное хранилище, как показано выше.
Проверка результата
После записи убедитесь, что файл действительно сохранился и восстановим. Самый надёжный способ — извлечь данные обратно и сравнить размер с исходным файлом, а для критичных данных — и контрольную сумму.
Как проверить результат
Возвращаются ДвоичныеДанные, размер совпадает с исходным файлом
Неопределено или размер равен нулю — запись не состоялась
Файл открывается штатной программой без ошибок
Файл повреждён или не открывается — проверьте, не записали ли вы строку вместо двоичных данных
Прирост соответствует суммарному объёму файлов с учётом сжатия
Резкий скачок размера — стоит пересмотреть подход к хранению
Дополнительно полезно вывести в форме признак наличия вложения — например, булеву отметку или размер файла, вычисляемый при записи. Это избавляет пользователей от необходимости «проверять наугад», есть ли файл внутри.
Что важно запомнить
Файл помещается через цепочку ДвоичныеДанные → Новый ХранилищеЗначения → запись объекта, извлекается методом Получить(). В клиент-серверном режиме файл передаётся через временное хранилище. Используйте сжатие, храните имя файла отдельно и не помещайте большие массивы данных в файловую базу.
Частые вопросы
Можно ли хранить в хранилище значений не файл, а другие данные?
Да. Хранилище значений принимает почти любые сериализуемые объекты: таблицы значений, структуры, массивы, картинки. Ограничение — данные должны поддерживать сериализацию платформой; например, COM-объекты и открытые соединения сохранить нельзя.
Как узнать размер файла внутри хранилища без извлечения?
Штатного способа прочитать размер без вызова Получить() нет. Практичное решение — при записи сохранять размер в отдельном числовом реквизите и использовать его для отображения и контроля.
Что делает параметр сжатия и всегда ли его использовать?
СжатиеДанных уменьшает объём данных при записи в базу. Для текстовых форматов, XML и несжатых изображений выигрыш значительный. Для JPG, ZIP и видео сжатие почти бесполезно, но и не вредит — его можно оставить включённым по умолчанию.
Можно ли найти все объекты, у которых хранилище пустое?
Да, запросом с условием вида «НЕ Хранилище ЕСТЬ NULL» проверяется заполненность, но содержимое сравнить нельзя. На практике удобнее завести отдельный булев реквизит «ЕстьВложение» и отбирать по нему — это работает быстрее и прозрачнее.
Чем хранилище значений отличается от временного хранилища?
Хранилище значений — тип данных для постоянного сохранения в реквизитах базы. Временное хранилище — механизм передачи данных между клиентом и сервером в рамках сеанса или формы; его содержимое удаляется автоматически и не предназначено для долговременного хранения.
Будет ли работать этот код в типовой конфигурации без доработок?
Код универсален для платформы 8.3, но реквизит с типом «ХранилищеЗначения» в типовом объекте придётся добавить через расширение или доработку конфигурации. Если конфигурация на БСП, рассмотрите штатный механизм присоединенных файлов — он уже реализует загрузку, версии и хранение в томах.