Разработка и Конфигурация · 12 мин
Как сохранить двоичные данные в файл в 1С
Как сохранить двоичные данные в файл в 1С: метод Записать(), работа с хранилищем значений, временными файлами и потоками. Примеры кода, ошибки и проверка результата.
Двоичные данные в 1С встречаются постоянно: картинки товаров, PDF-документы, подписанные файлы ЭДО, архивы, ответы веб-сервисов. Чтобы записать их на диск, используется встроенный тип ДвоичныеДанные и его метод Записать(), а для более гибких сценариев — потоки и объекты ЗаписьДанных. Выбор способа зависит от того, где лежат данные (в хранилище значений, во временном хранилище, в памяти) и на какой стороне выполняется код — на клиенте или на сервере.
Главная ловушка для начинающих разработчиков — попытка записать двоичные данные через текстовые механизмы (ЗаписьТекста, ТекстовыйДокумент). Это гарантированно портит файл: текстовые объекты перекодируют байты, и на выходе получается повреждённый документ, который не открывается. Для байтов нужны только двоичные инструменты.
Ниже разобраны рабочие сценарии: запись из хранилища значений, сохранение файла, полученного по HTTP, выгрузка из временного хранилища на клиенте, работа с большими файлами через потоки, а также проверка результата и типичные ошибки.
Базовый способ: метод Записать() объекта ДвоичныеДанные
Самый простой сценарий — двоичные данные уже есть в переменной. Тогда достаточно одной строки: вызвать метод Записать() и передать полное имя файла. Метод создаёт файл или полностью перезаписывает существующий без предупреждения.
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Исходные\картинка.jpg");
ДвоичныеДанные.Записать("C:\Выгрузка\картинка_копия.jpg");
Конструктор Новый ДвоичныеДанные(ИмяФайла) читает существующий файл с диска в память. Это удобно для копирования и передачи файлов между процедурами, но помните: всё содержимое загружается в оперативную память целиком. Для файлов в сотни мегабайт такой подход может привести к ошибке нехватки памяти, особенно в 32-разрядном сервере 1С.
Путь для записи должен существовать: метод Записать() не создаёт каталоги автоматически. Если папка отсутствует, будет вызвано исключение. Поэтому перед записью проверяйте каталог и при необходимости создавайте его через СоздатьКаталог().
Как выгрузить данные из хранилища значений
В типовых конфигурациях файлы чаще всего лежат в реквизитах типа ХранилищеЗначения — например, в справочниках присоединённых файлов или в реквизите «Картинка» номенклатуры. Хранилище значений — это упакованный контейнер, напрямую записать его в файл нельзя: сначала нужно извлечь содержимое методом Получить().
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ДанныеФайла ИЗ Справочник.ПрисоединенныеФайлы ГДЕ Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ФайлСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ДвоичныеДанные = Выборка.ДанныеФайла.Получить();
Если ДвоичныеДанные <> Неопределено Тогда
ДвоичныеДанные.Записать("C:\Выгрузка\" + ИмяФайла);
КонецЕсли;
КонецЕсли;
Обязательно проверяйте результат Получить() на Неопределено: пустое хранилище вернёт именно его, и попытка вызвать Записать() у Неопределено приведёт к ошибке «Значение не является значением объектного типа». Это одна из самых частых ошибок при выгрузке вложений.
Обратите внимание на тип результата. Хранилище значений может содержать не только ДвоичныеДанные, но и картинку (объект Картинка), табличный документ и другие сериализуемые значения. У объекта Картинка тоже есть метод Записать(ИмяФайла), но если нужен именно исходный байтовый поток, лучше хранить и извлекать ДвоичныеДанные.
Сохранение файла, полученного по HTTP или из веб-сервиса
При обмене с внешними системами файл часто приходит как тело HTTP-ответа. У объекта HTTPОтвет есть метод ПолучитьТелоКакДвоичныеДанные(), после которого запись выполняется стандартно. Важно не использовать ПолучитьТелоКакСтроку() для бинарных файлов — строка исказит байты.
Соединение = Новый HTTPСоединение("files.example.com");
ЗапросHTTP = Новый HTTPЗапрос("/download/report.pdf");
Ответ = Соединение.Получить(ЗапросHTTP);
Если Ответ.КодСостояния = 200 Тогда
ДвоичныеДанные = Ответ.ПолучитьТелоКакДвоичныеДанные();
ДвоичныеДанные.Записать(КаталогВременныхФайлов() + "report.pdf");
КонецЕсли;
Альтернатива без промежуточной переменной — метод HTTPСоединение.Получить() с параметром ИмяВыходногоФайла: платформа сама запишет тело ответа прямо в файл. Это экономит память при скачивании больших файлов и является предпочтительным способом, если данные не нужно дополнительно обрабатывать в коде.
Сравнение вариантов
Универсальный способ, когда данные уже в памяти. Ограничение: весь файл занимает оперативную память.
Подходит для скачивания больших файлов — запись идёт напрямую на диск, минуя память. Не подходит, если данные нужно изменить перед сохранением.
Гибкий вариант для больших файлов, склейки частей и записи по частям. Требует больше кода и аккуратного закрытия потоков.
Выгрузка файла на клиенте: временное хранилище и диалог сохранения
В управляемых формах типовой сценарий выглядит иначе: данные формируются на сервере, помещаются во временное хранилище, а на клиенте пользователь выбирает, куда сохранить файл. Прямая запись на диск клиента из серверного кода невозможна — сервер не имеет доступа к файловой системе пользователя.
Порядок действий такой: в серверной процедуре получить двоичные данные и вызвать ПоместитьВоВременноеХранилище(ДвоичныеДанные, УникальныйИдентификаторФормы). На клиенте — использовать НачатьПолучениеФайлаССервера() или ПолучитьФайл(), передав адрес хранилища и имя файла. В веб-клиенте вместо этого применяется ПолучитьФайл(), который инициирует скачивание через браузер.
- 1На сервере получите ДвоичныеДанные (из хранилища значений, запроса или генерации)
- 2Поместите данные во временное хранилище с привязкой к форме
- 3На клиенте вызовите НачатьПолучениеФайлаССервера() с адресом хранилища и именем файла
- 4В обработчике завершения проверьте, что файл получен без ошибки
- 5Проверьте результат: откройте сохранённый файл подходящей программой
Не пытайтесь на клиенте вызывать ДвоичныеДанные.Записать() с «прошивкой» пути вроде C:\Users\User\Desktop: в веб-клиенте это не сработает вовсе, а в тонком клиенте потребует прав и расширения работы с файлами. Асинхронные файловые методы — единственный корректный путь для совместимости со всеми видами клиентов.
Большие файлы: потоки и объект ЗаписьДанных
Когда файл велик или собирается из частей, используйте файловые потоки. Объект ФайловыеПотоки.ОткрытьДляЗаписи(ИмяФайла) (или ФайловыйПоток) позволяет писать данные порциями, а ЗаписьДанных добавляет удобные методы записи байтов, строк и целых буферов двоичных данных.
Поток = ФайловыеПотоки.ОткрытьДляЗаписи("C:\Выгрузка\большой_файл.dat");
ЗаписьДанных = Новый ЗаписьДанных(Поток);
Для Каждого Часть Из ЧастиДанных Цикл
ЗаписьДанных.ЗаписатьДвоичныеДанные(Часть);
КонецЦикла;
ЗаписьДанных.Закрыть();
Поток.Закрыть();
Поток обязательно нужно закрыть, желательно через конструкцию Попытка ... Исключение с закрытием в обоих ветках. Незакрытый поток держит файл заблокированным, и повторная запись в него или удаление будут невозможны до перезапуска сеанса. Также помните, что запись в поток может сбрасывать данные на диск с буферизацией — до вызова Закрыть() файл на диске может быть неполным.
Проверка результата и контрольные точки
После записи убедитесь, что файл действительно корректен. Минимальная проверка — существование файла и ненулевой размер через объект Файл. Для критичных обменов полезно сравнить размер записанного файла с размером исходных двоичных данных (метод Размер()) — расхождение означает обрыв записи.
Для файлов с известным форматом надёжнее проверка открытием: PDF должен открыться в просмотрщике, архив — распаковаться, картинка — отобразиться. Если файл «записался», но не открывается, почти наверняка данные на каком-то этапе прошли через строковое преобразование или Base64 без обратного декодирования.
Как проверить результат
Размер совпадает с размером исходных ДвоичныеДанные
Файл отсутствует или имеет нулевой размер
PDF, картинка или архив открываются без ошибок
Программа сообщает о повреждении файла — вероятно, данные прошли через строку
Запись выполняется без ошибок доступа
Ошибка «файл занят» — где-то не закрыт поток
Типичные ошибки и ограничения
Первая группа ошибок связана с преобразованиями: передача двоичных данных через строку, запись через ЗаписьТекста, забытое декодирование из Base64 (нужен явный вызов Base64Значение() перед записью). Любое из этих действий делает файл нечитаемым, причём внешне запись выглядит успешной.
Вторая группа — инфраструктурная: несуществующий каталог назначения, отсутствие прав на запись у пользователя, под которым работает сервер 1С, переполнение диска, блокировка файла антивирусом или другим процессом. Оборачивайте запись в Попытка/Исключение и логируйте текст ошибки — это сильно упрощает диагностику в регламентных заданиях, где исключение иначе просто теряется.
Что важно запомнить
Основной инструмент — метод ДвоичныеДанные.Записать(ИмяФайла). Из хранилища значений данные извлекаются через Получить() с проверкой на Неопределено. Для выгрузки пользователю используется временное хранилище и асинхронное получение файла на клиенте, для больших файлов — потоки. Результат всегда проверяйте по размеру и открытием файла.
Частые вопросы
Можно ли записать двоичные данные в файл в фоновом задании?
Да, и это рекомендуемый подход для тяжёлых выгрузок. Фоновое задание выполняется на сервере, поэтому путь должен быть доступен учётной записи сервера 1С. Результат (например, путь к файлу или адрес временного хранилища) верните через ПоместитьВоВременноеХранилище, чтобы клиентская часть могла его забрать.
Как сохранить файл в формате Base64?
Сначала преобразуйте строку Base64 в двоичные данные функцией Base64Значение(Строка) — она вернёт объект ДвоичныеДанные. Затем вызовите стандартный метод Записать(ИмяФайла). Если записать строку Base64 как текст, получится текстовый файл с закодированным содержимым, а не исходный документ.
Почему записанный файл не открывается, хотя ошибок не было?
Чаще всего данные где-то прошли через строковое преобразование: чтение тела HTTP-ответа как строки, запись через текстовые объекты или двойное кодирование Base64. Проверьте цепочку получения данных и убедитесь, что на каждом шаге используется тип ДвоичныеДанные без промежуточных строк.
Как узнать размер двоичных данных перед записью?
У объекта ДвоичныеДанные есть метод Размер(), возвращающий размер в байтах. Его удобно использовать для проверки свободного места на диске перед выгрузкой и для контроля результата — размер записанного файла должен совпадать с размером данных.
Работает ли ДвоичныеДанные.Записать() в веб-клиенте?
Нет, прямая запись на диск в веб-клиенте недоступна из соображений безопасности браузера. Правильная схема: данные помещаются во временное хранилище на сервере, а на клиенте вызывается ПолучитьФайл() — браузер инициирует стандартное скачивание файла пользователю.
Как дописать данные в конец существующего файла?
Метод Записать() всегда перезаписывает файл целиком. Для дозаписи откройте файловый поток в режиме добавления (ФайловыеПотоки.ОткрытьДляДобавления()) или создайте ФайловыйПоток с режимом открытия «Добавить», затем запишите данные через ЗаписьДанных и закройте поток.