Как в 1С сохранить файл на диск из двоичных данных, не зная его расширение?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Проанализируем ситуацию: у нас есть объект ДвоичныеДанные, который мы получили из другой системы. Для организации такого транспорта подойдёт готовый REST API для обмена данными в формате JSON и PDF. Нам нужно создать из него полноценный файл, но мы не знаем, был ли это .txt, .jpg, .pdf или любой другой из десятков возможных форматов. Рассмотрим несколько подходов к решению этой задачи, от простого и надежного до более сложного, но универсального.

Решение 1: Передача метаданных вместе с файлом

Это самый правильный и рекомендуемый способ. Идея заключается в том, чтобы не передавать "голые" двоичные данные, а упаковать их в структуру вместе со всей необходимой информацией: именем файла и его расширением. Для этой задачи есть подсистема обмена данными через HTTP, XML и FTP.

При передаче данных между базами через HTTP-сервисы принято использовать форматы JSON или XML — для этого есть конструктор обмена данными через web-сервисы с произвольным кодом. Создадим на стороне отправителя структуру, которая будет содержать всю необходимую информацию.

Рассмотрим по шагам на стороне отправителя:

  1. Получаем файл, который приложил пользователь, и считываем его в объект ДвоичныеДанные.
  2. Получаем информацию о файле, в частности его имя и расширение. Для этого можно использовать объект Файл.
  3. Для безопасной передачи по HTTP преобразуем двоичные данные в строку формата Base64. Это стандартный механизм, который представляет бинарные данные в виде текста, не искажая их.
  4. Формируем структуру или объект для сериализации в JSON, который будет содержать имя файла, расширение и данные в виде строки Base64.

Посмотрим на пример кода на стороне отправителя:


// Предположим, у нас есть путь к исходному файлу
ПутьКФайлу = "C:\Temp\МойДокумент.docx";

// Создаем объект Файл для получения информации
ИсходныйФайл = Новый Файл(ПутьКФайлу);
Если НЕ ИсходныйФайл.Существует() Тогда
    // Обработка ошибки: файл не найден
    Возврат;
КонецЕсли;

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

// Преобразуем двоичные данные в строку Base64
ДанныеBase64 = БазовыеФункцииПреобразования.ПолучитьСтрокуИзДвоичныхДанныхBase64(ДанныеФайла);

// Формируем структуру для отправки
СтруктураДляОтправки = Новый Структура;
СтруктураДляОтправки.Вставить("ИмяФайла", ИсходныйФайл.ИмяБезРасширения);
СтруктураДляОтправки.Вставить("Расширение", ИсходныйФайл.Расширение); // Например, ".docx"
СтруктураДляОтправки.Вставить("ДанныеФайла", ДанныеBase64);

// Сериализуем структуру в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураДляОтправки);
ТелоЗапросаJSON = ЗаписьJSON.Закрыть();

// Далее отправляем ТелоЗапросаJSON через HTTP-запрос...

Теперь разберем шаги на принимающей стороне:

  1. Получаем тело HTTP-запроса в виде строки JSON.
  2. Десериализуем JSON в структуру или соответствие 1С.
  3. Извлекаем из структуры имя, расширение и строку Base64.
  4. Выполняем обратное преобразование: из строки Base64 получаем исходные ДвоичныеДанные.
  5. Формируем полное имя файла и используем метод Записать() объекта ДвоичныеДанные для сохранения файла на диск.

Пример кода на стороне получателя:


// ТелоЗапросаJSON - строка, полученная от HTTP-сервиса

// Десериализуем JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТелоЗапросаJSON);
ПолученныеДанные = ПрочитатьJSON(ЧтениеJSON);

// Получаем компоненты
ИмяФайла = ПолученныеДанные.ИмяФайла;
Расширение = ПолученныеДанные.Расширение;
ДанныеBase64 = ПолученныеДанные.ДанныеФайла;

// Преобразуем строку Base64 обратно в двоичные данные
ДвоичныеДанные = БазовыеФункцииПреобразования.ПолучитьДвоичныеДанныеИзСтрокиBase64(ДанныеBase64);

// Формируем путь для сохранения
ПутьСохранения = "D:\Archive\";
ПолноеИмяФайла = ПутьСохранения + ИмяФайла + Расширение;

// Сохраняем файл на диск
ДвоичныеДанные.Записать(ПолноеИмяФайла);

Сообщить("Файл успешно сохранен: " + ПолноеИмяФайла);

Этот метод является самым надежным, так как исключает двусмысленность и не требует сложных алгоритмов для угадывания типа файла.

Решение 2: Определение типа файла по его сигнатуре

Бывают ситуации, когда мы не можем изменить протокол обмена и получаем только двоичные данные без какой-либо метаинформации. В этом случае можно попытаться определить тип файла по его сигнатуре (также известной как "магические числа").

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

Алгоритм определения типа файла по сигнатуре:

  1. Получив ДвоичныеДанные, нужно прочитать их первые несколько байт. Для этого удобно использовать объекты ПотокВПамяти и ЧтениеДанных.
  2. Сравнить прочитанную последовательность байтов с известными сигнатурами. Для этого нужно заранее подготовить справочник или соответствие "сигнатура -> расширение".
  3. Если совпадение найдено, использовать соответствующее расширение для сохранения файла.

Посмотрим на пример реализации:


Функция ОпределитьРасширениеПоСигнатуре(ДвоичныеДанные)
    
    // Создаем справочник сигнатур (можно вынести в отдельный модуль или справочник)
    Сигнатуры = Новый Соответствие;
    Сигнатуры.Вставить("89504E47", ".png");
    Сигнатуры.Вставить("FFD8FF", ".jpg");
    Сигнатуры.Вставить("25504446", ".pdf");
    Сигнатуры.Вставить("504B0304", ".zip"); // Может быть и docx, xlsx и т.д.
    Сигнатуры.Вставить("47494638", ".gif");

    // Создаем поток для чтения двоичных данных
    Поток = Новый ПотокВПамяти(ДвоичныеДанные);
    ЧтениеДанных = Новый ЧтениеДанных(Поток);

    // Читаем первые 4 байта (длины сигнатуры может быть разной, здесь для примера)
    Буфер = ЧтениеДанных.Прочитать(4);
    ЧтениеДанных.Закрыть();
    Поток.Закрыть();

    // Преобразуем байты в строку HEX для сравнения
    СтрокаHEX = "";
    Для Индекс = 0 По Буфер.Размер() - 1 Цикл
        СтрокаHEX = СтрокаHEX + Формат(Буфер.Получить(Индекс), "ЧГ=02, ЧН=; ЧВН=; ЧГ=2");
    КонецЦикла;

    // Ищем сигнатуру в нашем справочнике
    Для Каждого КлючЗначение Из Сигнатуры Цикл
        Если Лев(СтрокаHEX, СтрДлина(КлючЗначение.Ключ)) = Врег(КлючЗначение.Ключ) Тогда
            Возврат КлючЗначение.Значение;
        КонецЕсли;
    КонецЦикла;

    // Если ничего не найдено, возвращаем пустое расширение или ".bin"
    Возврат ".bin";
    
КонецФункции

// Пример использования
НеизвестныеДвоичныеДанные = ПолучитьДанныеИзДругойБазы(); // Получаем наши данные
ОпределенноеРасширение = ОпределитьРасширениеПоСигнатуре(НеизвестныеДвоичныеДанные);

ИмяСохраняемогоФайла = "Файл_" + Формат(ТекущаяДата(), "ДФ=ггггММдд_ччммсс");
ПолноеИмяФайла = "D:\Archive\" + ИмяСохраняемогоФайла + ОпределенноеРасширение;

НеизвестныеДвоичныеДанные.Записать(ПолноеИмяФайла);

Важно понимать, что этот метод не дает 100% гарантии. Некоторые форматы не имеют четкой сигнатуры, а некоторые, как ZIP-архивы, могут представлять разные типы файлов (docx, xlsx, jar). Однако для ограниченного набора известных форматов этот способ вполне рабочий.

Итоги и рекомендации

Проанализировав оба подхода, сделаем выводы:

  1. Приоритетный способ — передача метаданных (имени и расширения) вместе с содержимым файла. Это просто, надежно и является отраслевым стандартом при разработке интеграций. Используйте JSON или XML и кодируйте двоичные данные в Base64.
  2. Резервный способ — определение типа файла по сигнатуре. Используйте его, только если вы не контролируете систему-отправитель и не можете договориться об изменении формата передачи данных. Этот метод требует поддержки справочника сигнатур и не всегда точен.

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

← На главную