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

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

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

Как сохранить файл из хранилища значений в 1С на диск: метод Получить(), ДвоичныеДанные, запись через Записать(), примеры кода, типичные ошибки и проверка результата.

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

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

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

Как устроено хранилище значений и что в нём может лежать

Реквизит типа «ХранилищеЗначения» — это контейнер, в который платформа упаковывает произвольное значение: двоичные данные, строку, картинку, структуру, таблицу значений и даже сериализуемые объекты. Перед записью значение помещается конструктором Новый ХранилищеЗначения(Значение), а при чтении извлекается методом Получить(). Пока вы не вызвали Получить(), вы не знаете, что реально лежит внутри.

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

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

Базовый код: извлечь двоичные данные и записать файл

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

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

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

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

ВызватьИсключение "Хранилище пустое, файл не загружен";

КонецЕсли;

Если ТипЗнч(Значение) <> Тип("ДвоичныеДанные") Тогда

ВызватьИсключение "В хранилище не двоичные данные: " + ТипЗнч(Значение);

КонецЕсли;

Значение.Записать("C:\Temp\Выгрузка.pdf");

Метод Записать() создаёт файл и перезаписывает его, если файл с таким именем уже существует. Каталог по указанному пути должен существовать заранее — платформа не создаёт вложенные папки автоматически. Если каталога нет, будет вызвано исключение, поэтому перед записью полезно проверить каталог через объект Файл и при необходимости создать его методом СоздатьКаталог().

Если в хранилище лежит значение типа Картинка, у него есть собственный метод Записать(ИмяФайла), который сохраняет изображение в исходном формате. Для строки с текстом (например, XML или CSV) используйте ТекстовыйДокумент с явным указанием кодировки, иначе кириллица может сохраниться в неправильной кодировке.

Порядок действий
  1. 1
    Получите объект и прочитайте реквизит с хранилищем значений
  2. 2
    Вызовите метод Получить() и проверьте результат на Неопределено
  3. 3
    Убедитесь, что тип значения — ДвоичныеДанные
  4. 4
    Проверьте или создайте каталог назначения
  5. 5
    Вызовите Записать() с полным путём и именем файла
  6. 6
    Откройте файл и убедитесь, что он читается и имеет ненулевой размер

Где выполнять код: сервер, толстый и тонкий клиент

Место выполнения кода определяет, на чьём диске окажется файл. В серверной процедуре Записать() создаст файл на диске сервера 1С, а не на компьютере пользователя. Это частая причина «пропавших» файлов: обработка отработала без ошибок, а файл лежит в каталоге на сервере, куда у пользователя нет доступа.

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

&НаСервере

Функция ПолучитьАдресДанных(Ссылка)

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

Возврат ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);

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

&НаКлиенте

Процедура СохранитьНаДиск(Команда)

Адрес = ПолучитьАдресДанных(Объект.Ссылка);

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

Данные.Записать("C:\Users\User\Documents\Файл.pdf");

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

В веб-клиенте прямая запись по произвольному пути недоступна: браузер не даёт коду работать с файловой системой напрямую. Там используется механизм получения файла через ПолучитьФайл() или НачатьПолучениеФайлов(), который инициирует скачивание через браузер, либо расширение для работы с файлами. Учитывайте это при проектировании обработок, которые должны работать во всех режимах.

Как определить имя и расширение файла

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

Если реквизита с расширением нет, расширение иногда можно определить по сигнатуре (первым байтам) двоичных данных: PDF начинается с %PDF, ZIP-архивы и документы Office — с PK, JPEG — с байтов FF D8. Это эвристика, а не гарантия, поэтому полагаться на неё стоит только как на запасной вариант. Надёжнее при загрузке файлов в базу всегда сохранять исходное имя в отдельном реквизите.

При формировании имени удалите или замените символы \ / : * ? " < > | — они запрещены в Windows и вызовут ошибку записи. Также следите за длиной полного пути: при очень длинных именах и глубокой вложенности каталогов запись может завершиться ошибкой из-за ограничения длины пути в файловой системе.

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

Массовая выгрузка файлов из базы

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

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

Полезно вести журнал выгрузки: записывать в табличный документ или текстовый файл соответствие «ссылка — путь к файлу — результат». Это позволит после завершения быстро найти объекты, где выгрузка не удалась, и повторить операцию точечно, а не запускать весь процесс заново.

Перед массовой выгрузкой сделайте пробный прогон на 5–10 объектах и откройте полученные файлы вручную. Это дешёвый способ поймать ошибки в именах, расширениях и правах доступа до того, как обработана вся база.
ПЗ
Практическое замечание

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

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

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

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

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

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

Файл существует по указанному пути
Норма

Файл на месте, размер больше нуля

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

Файла нет или размер 0 байт

Размер файла и размер двоичных данных
Норма

Значения совпадают

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

Размеры различаются — запись неполная

Открытие в штатной программе
Норма

Файл открывается и отображается корректно

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

Ошибка открытия или «формат не поддерживается»

Расширение файла
Норма

Совпадает с реальным форматом содержимого

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

Расширение не соответствует содержимому

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

Метод Получить() вернул Неопределено. Что это значит?

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

Как сохранить файл из хранилища в веб-клиенте?

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

Файл записывается, но не открывается. В чём причина?

Чаще всего не совпадает расширение и реальный формат: например, в хранилище лежит PNG, а файл записан как .jpg. Проверьте реквизит с исходным расширением или сигнатуру данных. Вторая причина — в хранилище лежит не файл, а сериализованное значение, и его нужно преобразовать перед записью.

Можно ли сохранить файл без кода, штатными средствами?

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

Где искать файл, если запись выполнялась на сервере?

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

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

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

Итог

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

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