В процессе интеграции систем 1С часто возникает задача передачи файлов из одной базы в другую — для этого подойдёт универсальная обработка переноса данных через HTTP и файлы. Файл считывается, преобразуется в двоичные данные, передается, например, через HTTP-сервис, и на принимающей стороне его нужно снова сохранить на диск. Основная сложность заключается в том, что после преобразования в двоичные данные информация о первоначальном имени и расширении файла теряется. Давайте разберем, как правильно организовать этот процесс и восстановить файл в его исходном виде.
Проанализируем ситуацию: у нас есть объект ДвоичныеДанные, который мы получили из другой системы. Для организации такого транспорта подойдёт готовый REST API для обмена данными в формате JSON и PDF. Нам нужно создать из него полноценный файл, но мы не знаем, был ли это .txt, .jpg, .pdf или любой другой из десятков возможных форматов. Рассмотрим несколько подходов к решению этой задачи, от простого и надежного до более сложного, но универсального.
Это самый правильный и рекомендуемый способ. Идея заключается в том, чтобы не передавать "голые" двоичные данные, а упаковать их в структуру вместе со всей необходимой информацией: именем файла и его расширением. Для этой задачи есть подсистема обмена данными через HTTP, XML и FTP.
При передаче данных между базами через HTTP-сервисы принято использовать форматы JSON или XML — для этого есть конструктор обмена данными через web-сервисы с произвольным кодом. Создадим на стороне отправителя структуру, которая будет содержать всю необходимую информацию.
Рассмотрим по шагам на стороне отправителя:
ДвоичныеДанные.Файл.Посмотрим на пример кода на стороне отправителя:
// Предположим, у нас есть путь к исходному файлу
ПутьКФайлу = "C:\Temp\МойДокумент.docx";
// Создаем объект Файл для получения информации
ИсходныйФайл = Новый Файл(ПутьКФайлу);
Если НЕ ИсходныйФайл.Существует() Тогда
// Обработка ошибки: файл не найден
Возврат;
КонецЕсли;
// Считываем файл в ДвоичныеДанные
ДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
// Преобразуем двоичные данные в строку Base64
ДанныеBase64 = БазовыеФункцииПреобразования.ПолучитьСтрокуИзДвоичныхДанныхBase64(ДанныеФайла);
// Формируем структуру для отправки
СтруктураДляОтправки = Новый Структура;
СтруктураДляОтправки.Вставить("ИмяФайла", ИсходныйФайл.ИмяБезРасширения);
СтруктураДляОтправки.Вставить("Расширение", ИсходныйФайл.Расширение); // Например, ".docx"
СтруктураДляОтправки.Вставить("ДанныеФайла", ДанныеBase64);
// Сериализуем структуру в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураДляОтправки);
ТелоЗапросаJSON = ЗаписьJSON.Закрыть();
// Далее отправляем ТелоЗапросаJSON через HTTP-запрос...
Теперь разберем шаги на принимающей стороне:
ДвоичныеДанные.Записать() объекта ДвоичныеДанные для сохранения файла на диск.Пример кода на стороне получателя:
// ТелоЗапросаJSON - строка, полученная от HTTP-сервиса
// Десериализуем JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТелоЗапросаJSON);
ПолученныеДанные = ПрочитатьJSON(ЧтениеJSON);
// Получаем компоненты
ИмяФайла = ПолученныеДанные.ИмяФайла;
Расширение = ПолученныеДанные.Расширение;
ДанныеBase64 = ПолученныеДанные.ДанныеФайла;
// Преобразуем строку Base64 обратно в двоичные данные
ДвоичныеДанные = БазовыеФункцииПреобразования.ПолучитьДвоичныеДанныеИзСтрокиBase64(ДанныеBase64);
// Формируем путь для сохранения
ПутьСохранения = "D:\Archive\";
ПолноеИмяФайла = ПутьСохранения + ИмяФайла + Расширение;
// Сохраняем файл на диск
ДвоичныеДанные.Записать(ПолноеИмяФайла);
Сообщить("Файл успешно сохранен: " + ПолноеИмяФайла);
Этот метод является самым надежным, так как исключает двусмысленность и не требует сложных алгоритмов для угадывания типа файла.
Бывают ситуации, когда мы не можем изменить протокол обмена и получаем только двоичные данные без какой-либо метаинформации. В этом случае можно попытаться определить тип файла по его сигнатуре (также известной как "магические числа").
Сигнатура — это уникальная последовательность байтов в самом начале файла, которая характерна для определенного формата. Например:
FF D8 FF.89 50 4E 47 0D 0A 1A 0A.%PDF- (в hex 25 50 44 46 2D).50 4B 03 04.Алгоритм определения типа файла по сигнатуре:
ДвоичныеДанные, нужно прочитать их первые несколько байт. Для этого удобно использовать объекты ПотокВПамяти и ЧтениеДанных.Посмотрим на пример реализации:
Функция ОпределитьРасширениеПоСигнатуре(ДвоичныеДанные)
// Создаем справочник сигнатур (можно вынести в отдельный модуль или справочник)
Сигнатуры = Новый Соответствие;
Сигнатуры.Вставить("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). Однако для ограниченного набора известных форматов этот способ вполне рабочий.
Проанализировав оба подхода, сделаем выводы:
В любом случае, для записи файла на диск из готового объекта ДвоичныеДанные используется один и тот же финальный шаг: вызов метода ДвоичныеДанные.Записать(ПолноеИмяФайла). Главная задача — правильно сформировать это полное имя.