Как прочитать составной ответ в ColdFusion

Я делаю сообщение CFHTTP для веб-службы, которая возвращает две части (составные), XML и PDF. Я ищу, чтобы получить только PDF. Мой cfhttp.filecontent относится к типу java.io.ByteArrayOutputStream. Когда я делаю toString(), я получаю следующее

Часть 1

Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit

Часть 2

Content-Type: application/pdf
Content-Transfer-Encoding: binary

Я получаю ответ в cfhttp.fileContent, и данные выглядят следующим образом

--MIME_Boundary
Content-ID: <aa82dfa.N51ec355b.3.15b86044531.59d6>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">....</soapenv:Envelope>
--MIME_Boundary
Content-Id: <[email protected]>
Content-Type: application/pdf
Content-Transfer-Encoding: binary

%PDF-1.4
%ÈÁÄ×
<content removed>
25081
%%EOF

--MIME_Boundary--

Я попытался удалить все данные, не относящиеся к PDF, но это все равно неправильный двоичный файл.

есть идеи?

Из комментариев

Когда я делаю cfdump на fileContent, я получаю следующее:

Class Name: java.io.ByteArrayOutputStream 
Methods: 
    close() returns void 
    reset() returns void 
    size() returns int 
    toByteArray() returns byte[] 
    toString(java.lang.String) returns java.lang.String 
    toString() returns java.lang.String 
    toString(int) returns java.lang.String 
    write(byte[], int, int) returns void 
    write(int) returns void 
    writeTo(java.io.OutputStream) returns void

Когда я вызываю toByteArray(), я получаю двоичные данные. Затем я сохраняю данные в файл и вижу части файла в формате XML и PDF.


person cma0651    schedule 24.04.2017    source источник
comment
Вместо преобразования переменной cfhttp.filecontent с помощью toString() попробуйте сбросить ее. Это покажет вам структуру, которую ColdFusion создает для вас, храня каждую часть информации. <cfdump var="#cfhttp.filecontent#" /> или в скрипте writeDump(cfhttp.filecontent); . Скопируйте этот вывод на свой вопрос выше. Как только мы узнаем поле, содержащее фактические двоичные данные, вам нужно будет декодировать base64 обратно в двоичный код перед представлением пользователю. binaryDecode. Что-то вроде <cfset binaryData=binaryDecode(cfhttp.filecontent.body,"Base64") />   -  person Miguel-F    schedule 24.04.2017
comment
Спасибо за ваш ответ. Когда я делаю cfdump для fileContent, я получаю следующее: Class Name: java.io.ByteArrayOutputStream Methods: close() returns void reset() returns void size() returns int toByteArray() returns byte[] toString(java.lang.String) returns java.lang.String toString() returns java.lang.String toString(int) returns java.lang.String write(byte[], int, int) returns void write(int) returns void writeTo(java.io.OutputStream) returns void   -  person cma0651    schedule 25.04.2017
comment
Когда я вызываю toByteArray(), я получаю двоичные данные. Затем я сохраняю данные в файл и вижу части файла в формате xml и pdf. Если я возьму результаты и выполню binaryDecode, я получу ByteArray objects cannot be converted to strings.. BinaryEncode работает.   -  person cma0651    schedule 25.04.2017
comment
Можете ли вы дать нам URL-адрес для публикации, чтобы мы могли попробовать?   -  person Jules    schedule 25.04.2017
comment
Если по какой-то причине он не является общедоступным, это полное содержимое файла выше? (Я понимаю, что содержимое обрезано для краткости, но я бы ожидал дополнительных заголовков...)   -  person Leigh    schedule 25.04.2017
comment
Предоставленный вами дамп, похоже, не относится к вызову cfhttp. Больше похоже на звонок cfinvoke??? Можете ли вы предоставить код, который вы используете для вызова этой службы?   -  person Miguel-F    schedule 25.04.2017
comment
Джулс, это внутренний код моей компании. Мигель-Ф, это дамп из cfhttp.filecontent Обычно я получаю обычные текстовые результаты, но я подозреваю, что, поскольку это многочастный ответ, я получил объект Java. Ли, поскольку в коде есть PHI-информация, я не могу предоставить все подробности, но могу заверить, что единственным содержимым, которое я удалил, был некоторый xml в части 1 и двоичный файл PDF во второй части.   -  person cma0651    schedule 26.04.2017
comment
@Chris - составной ответ имеет очень специфический формат. Инструменты, обычно используемые для его анализа, требуют наличия определенной информации, такой как заголовок, указывающий, что содержимое, за которым следует является составным, и используемый маркер границы. Я пытаюсь выяснить, действительно ли cfhttp удаляет эту информацию или вы просто отбросили ее как не относящуюся к делу. Есть и другие способы использования веб-сервисов, которые могут быть проще, а могут и нет... Все зависит от вашего кода. Было бы действительно полезно увидеть а) ваш вызов cfhttp и б) полный дамп ответа cfhttp (включая заголовки) — разумеется, отредактируйте все конфиденциальное.   -  person Leigh    schedule 26.04.2017
comment
Почитал. Предполагая, что вы можете динамически определить значение пограничного маркера. Попробуйте этот пример. gist.github.com/anonymous/5610b421abad1733c9a359d6bff8a068 . Обратите внимание, что приведенные выше данные ответа недействительны. Между Content-Transfer-Encoding: 8bit и xml должна быть новая строка. (Я предполагаю, что он был обрезан случайно.)   -  person Leigh    schedule 27.04.2017
comment
@Ли, спасибо, это одна часть головоломки, которая привела меня к моему решению! Я публикую свой ответ сейчас.   -  person cma0651    schedule 28.04.2017
comment
(Изменить) @Chris - Я вижу, ты выбрал другой путь. Если у вас есть время, не могли бы вы попробовать предложенный пример? Это должно работать нормально, но у меня нет веб-службы MTOM для подтверждения.   -  person Leigh    schedule 28.04.2017
comment
@Ли, я сделаю это завтра. Ваше предложение пришло после того, как я получил решение, которое я опубликовал.   -  person cma0651    schedule 28.04.2017
comment
@ Крис - Это было бы здорово. Мне очень любопытно, как обрабатывать этот тип контента, то есть mtom (кстати, я еще не видел вашего ответа, когда отправил последний комментарий, и не понял, что вы пошли совершенно в другом направлении :-)   -  person Leigh    schedule 28.04.2017
comment
@Leigh - Ваша ссылка на gitgub действительно исправила это с 1 небольшим изменением! Я обновлю свой ответ через несколько минут.   -  person cma0651    schedule 28.04.2017


Ответы (1)


Обходной путь потребовал двух изменений: изменения для установки принятого значения кодировки на gzip, deflate и для работы с двоичными данными с использованием java.

<cfhttpparam type="HEADER" name="Accept-Encoding" value="gzip,deflate">

Во-вторых, мне нужно было манипулировать ответом, используя бинарные методы.

binResponse = result.fileContent.toByteArray();

Затем я использовал утилиту от Бена Наделя, Binary.cfc, в котором есть все необходимые мне бинарные операции. Я использовал метод binarySlice() для извлечения начальной и конечной части двоичного файла. Нарезанные данные содержат двоичный файл именно в том формате, который мне был нужен. Это был не base64 или какой-либо другой тип, это был двоичный код.

sliced = binNadel.binarySlice( binResponse, <int posistion to start slice>, <int length of binary>));

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

Обновление:

Затем я просмотрел пример Ли, чтобы увидеть если бы я мог упростить свой код. Они предложили использовать класс Java MimeMultipart, который поддерживает анализ составного ответа MTOM. Вот окончательный рабочий код:

<cfscript>
    // Modify path as needed
    saveToDirec = "c:\temp\";

    // Hard coded "boundary" value for DEMO purposes. It MUST match actual value used in cfhttp response
    // Best to use cfhttp.responseHeader.content-Type so [if] the service changes your code won't break.
    contentType = "multipart/related; boundary=MIME_Boundary;";  

    // Load and parse ByteArrayOutputStream returned by CFHTTP
    dataSource = createObject("java", "javax.mail.util.ByteArrayDataSource").init(m_strSoapResponse.fileContent.toByteArray(), javaCast( "string", contentType));
    mimeParts = createObject("java", "javax.mail.internet.MimeMultipart").init(dataSource);

    for (i = 0; i < mimeParts.getCount(); i++) {
        writeOutput("<br>Processing part["& i &"]");
        bp = mimeParts.getBodyPart( javacast("int", i));

        // If this part is a PDF, save it to a file.
        if (!isNull(bp) && bp.isMimeType("application/pdf")) {
            outputFile = createObject("java", "java.io.File").init(saveToDirec &"demo_savedfile_"& i &".pdf");
            bp.saveFile(outputFile);
            writeOutput("<br>Saved: "& outputFile.getAbsolutePath());
        }
    }
</cfscript>

Спасибо всем за ваш вклад!

person cma0651    schedule 28.04.2017
comment
Интересный. Меня интересуют ответы mtom, так что пара вопросов :) 1. Какой тип объекта вы получите после добавления gzip,deflate - бинарный массив? Если да, имеет ли он тот же составной контент, что и раньше? 2. Как вы определяете начальную/конечную позицию? Если бы это могло быть динамическим (как с MimeMultipart), это было бы хорошим решением.3. Что касается пограничного маркера, вы выводили результат cfhttp и проверяли другие заголовки? Может быть включен в тип содержимого. - person Leigh; 28.04.2017
comment
Рад, что это сработало. К вашему сведению, С.О. Темы заархивированы для всеобщего блага, поэтому я внес некоторые изменения, чтобы включить больше информации для будущих читателей :) (Также добавлена ​​ссылка на то, где я читал об этой идее, чтобы отдать должное оригинальному автору). Не стесняйтесь вносить изменения. - person Leigh; 28.04.2017