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

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

Как записать двоичные данные в файл в 1С

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

Двоичные данные в 1С — это любые файлы, которые платформа не интерпретирует как текст: картинки, PDF-документы, архивы, выгрузки обменов, подписанные файлы ЭДО. Работа с ними строится вокруг встроенного типа ДвоичныеДанные, который умеет читать содержимое файла с диска и записывать его обратно методом Записать(). Базовый сценарий умещается в несколько строк кода, но на практике задача усложняется: данные могут приходить из хранилища значений, из буфера двоичных данных, из Base64-строки или из временного хранилища после загрузки с клиента.

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

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

Объект ДвоичныеДанные: базовый способ записи в файл

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

bsl
ДвоичныеДанные = Новый ДвоичныеДанные("C:\Temp\исходный.pdf");
ДвоичныеДанные.Записать("D:\Архив\копия.pdf");

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

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

Запись данных из хранилища значений и реквизитов

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

bsl
Запрос = Новый Запрос("ВЫБРАТЬ Файл ИЗ Справочник.ПрисоединенныеФайлы ГДЕ Ссылка = &Ссылка");
Запрос.УстановитьПараметр("Ссылка", СсылкаНаФайл);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
    Данные = Выборка.Файл.Получить();
    Если Данные <> Неопределено Тогда
        Данные.Записать("C:\Temp\выгрузка.bin");
    КонецЕсли;
КонецЕсли;

Обязательно проверяйте результат Получить() на Неопределено: если реквизит пустой или в хранилище записано значение другого типа, попытка вызвать Записать() приведёт к ошибке. Для надёжности можно дополнительно проверить тип через ТипЗнч(Данные) = Тип(\"ДвоичныеДанные\").

Если файл хранится в типовом механизме присоединённых файлов (БСП), предпочтительнее использовать готовые функции общего модуля РаботаСФайлами — они корректно обрабатывают тома хранения, версии файлов и права доступа, чего ручной код не делает.

Запись из буфера двоичных данных и Base64

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

bsl
Буфер = Новый БуферДвоичныхДанных(4);
Буфер.ЗаписатьЦелое32(0, 123456, ПорядокБайтов.LittleEndian);
Данные = Буфер.ПолучитьДвоичныеДанные();
Данные.Записать("C:\Temp\packet.bin");

Обратная ситуация — данные приходят извне в виде строки Base64, например из веб-сервиса или HTTP-API. Для преобразования используются функции Base64Значение() и Base64Строка(). Первая возвращает ДвоичныеДанные, готовые к записи; вторая, наоборот, кодирует двоичные данные в строку для передачи.

bsl
СтрокаBase64 = ОтветСервиса.ПолучитьТелоКакСтроку();
Данные = Base64Значение(СтрокаBase64);
Данные.Записать(КаталогВременныхФайлов() + "полученный.pdf");
Типичные ошибки
01
Передача в Base64Значение() строки с префиксом data:image/png;base64 — префикс нужно отрезать, иначе получите исключение или повреждённый файл.
02
Запись результата ХранилищеЗначения.Получить() без проверки на Неопределено — ошибка «Значение не является значением объектного типа».
03
Использование ЗаписьТекста для двоичных данных — текстовый объект перекодирует байты, и файл будет испорчен.
04
Запись на клиенте по серверному пути в клиент-серверной базе — файл не найден, потому что диски клиента и сервера разные.

Запись больших файлов: потоки и ЗаписьДанных

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

bsl
ПотокЗаписи = ФайловыеПотоки.ОткрытьДляЗаписи("D:\Big\archive.zip");
Запись = Новый ЗаписьДанных(ПотокЗаписи);
Запись.ЗаписатьБуферДвоичныхДанных(Буфер);
Запись.Закрыть();
ПотокЗаписи.Закрыть();

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

Сравнение способов записи

ДвоичныеДанные.Записать()

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

ФайловыйПоток + ЗаписьДанных

Для больших файлов и порционной записи. Требует аккуратного закрытия потоков.

РаботаСФайлами (БСП)

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

Клиент-серверный режим: передача файла и запись на сервере

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

bsl
// На сервере
Процедура СохранитьФайлНаСервере(АдресХранилища, ИмяФайла) Экспорт
    Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
    Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
        Данные.Записать("/var/1c/uploads/" + ИмяФайла);
    КонецЕсли;
КонецПроцедуры

Учитывайте права учётной записи, под которой работает сервер 1С (обычно это отдельная служебная учётная запись, а не пользователь): у неё должны быть права на запись в целевой каталог. Ошибка «Отказано в доступе» при корректном коде почти всегда означает именно проблему прав на каталог.

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

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

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

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

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

Существование файла
Норма

Новый Файл(Путь).Существует() возвращает Истина

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

Файла нет — проверяйте путь, права и место выполнения кода

Размер файла
Норма

Размер совпадает с Данные.Размер()

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

Размер меньше или нулевой — данные повреждены при передаче или Base64 декодирован с ошибкой

Открытие файла
Норма

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

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

Программа сообщает о повреждении — вероятна запись текстовым объектом или лишний префикс в Base64

Если файл записывается, но не открывается, чаще всего виновата промежуточная текстовая обработка: чтение через ЧтениеТекста, передача через строковый реквизит без Base64, ручная конкатенация строк. Двоичные данные нельзя пропускать через строковые переменные без кодирования — любое перекодирование необратимо портит байты.

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

Чем ДвоичныеДанные отличаются от БуфераДвоичныхДанных?

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

Почему файл записывается, но не открывается?

Наиболее вероятные причины: данные прошли через текстовую обработку (ЧтениеТекста, строковый реквизит) и были перекодированы; Base64-строка содержала служебный префикс вида data:...;base64, который не был отрезан; или в хранилище значений лежал не файл, а другой объект, сериализованный платформой.

Как записать файл, если нужно дописать данные в конец существующего?

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

Можно ли записать файл напрямую на диск пользователя из серверной процедуры?

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

Что делать с ошибкой «Отказано в доступе» при записи на сервере?

Проверьте права учётной записи службы сервера 1С на целевой каталог — она отличается от учётной записи пользователя. Также убедитесь, что путь существует, файл не открыт другим процессом и не стоит атрибут «только чтение».

Как передать двоичные данные через строку, например в XML или JSON?

Используйте кодирование Base64: функция Base64Строка() преобразует двоичные данные в безопасную для текстовых форматов строку, а Base64Значение() на приёмной стороне восстанавливает исходные байты. Прямая вставка двоичных данных в текст испортит файл.

Итог

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

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