How to send content type "multipart / related" in PHP

I need to submit multi-page / related content with PHP. The content will contain 3 files (2x XML, 1xPDF) XML files must be encoded as 7 bits PDF file must be encoded as base64

I can create such a file, but then I can’t figure out how to send it with curl in PHP.

The content should be something like this (I removed most of the encoded pdf). This exapmle comes from another closed source application:

MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_0_869724450.1481019442425" ------=_Part_0_869724450.1481019442425 Content-Type: application/vnd.cip4-jmf+xml; name=SubmitQueueEntry.jmf Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=SubmitQueueEntry.jmf Content-ID: <5cba3621:158d3a34526: -7ffe@hp.com > Content-Length: 465 <?xml version="1.0" encoding="UTF-8"?> <JMF xmlns="http://www.CIP4.org/JDFSchema_1_1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SenderID="Alces 0.9.9.1" TimeStamp="2012-08-21T14:55:08-06:00" Version="1.3"> <Command ID="ALCES_YECIYJ_4_20120821145508" Type="SubmitQueueEntry" xsi:type="CommandSubmitQueueEntry"> <QueueSubmissionParams ReturnJMF="http://YOURHOSTNAME:9090/jmf" URL="cid:5cba3621:158d3a34526: -7fff@hp.com " /> </Command> </JMF> ------=_Part_0_869724450.1481019442425 Content-Type: application/vnd.cip4-jdf+xml; name=test.pdf.jdf Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=test.pdf.jdf Content-ID: <5cba3621:158d3a34526: -7fff@hp.com > Content-Length: 1536 <?xml version="1.0" encoding="UTF-8"?> <JDF xmlns="http://www.CIP4.org/JDFSchema_1_1" Type="Combined" ID="rootNodeId" Status="Waiting" JobPartID="My Job Part ID" Version="1.3" Types="DigitalPrinting" DescriptiveName="My Job" JobID="My Job ID"> <Comment Name="JobSpec">Photobook</Comment><ResourcePool> <Media Class="Consumable" ID="M001" Status="Available" /> <DigitalPrintingParams Class="Parameter" ID="DPP001" Status="Available" /> <RunList ID="RunList_1" Status="Available" Class="Parameter"> <LayoutElement> <FileSpec MimeType="application/pdf" URL="cid:5cba3621:158d3a34526: -8000@hp.com " /> </LayoutElement> </RunList> <Component ID="Component" ComponentType="FinalProduct" Status="Unavailable" Class="Quantity" /> <NodeInfo ID="NI001" Class="Parameter" Status="Available" LastEnd="2015-01-21T13:14:40" JobPriority="50"><Comment Name="Instructions">Emboss with gold stitch</Comment><GeneralID IDUsage="EmbossText" IDValue="Sara and Michael Wedding,EmbossFontSize 20pt" /></NodeInfo><CustomerInfo Class="Parameter" ID="CI001" Status="Available" /></ResourcePool> <ResourceLinkPool> <MediaLink rRef="M001" Usage="Input" /> <DigitalPrintingParamsLink rRef="DPP001" Usage="Input" /> <RunListLink rRef="RunList_1" Usage="Input" /> <ComponentLink Usage="Output" rRef="Component" Amount="1" /> <NodeInfoLink rRef="NI001" Usage="Input" /><CustomerInfoLink rRef="CI001" Usage="Input" /></ResourceLinkPool> </JDF> ------=_Part_0_869724450.1481019442425 Content-Type: application/octet-stream; name=_113HN_test.pdf Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=_113HN_test.pdf Content-ID: <5cba3621:158d3a34526: -8000@hp.com > Content-Length: 576230 JVBERi0xLjUNJeLjz9MNCjEgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvT0NQcm9wZXJ0aWVzPDwv RDw8L09OWzUgMCBSXS9PcmRlciA2IDAgUi9SQkdyb3Vwc1tdPj4vT0NHc1s1IDAgUl0+Pi9QYWdl cyAzIDAgUi9UeXBlL0NhdGFsb2c+Pg1lbmRvYmoNMiAwIG9iag08PC9MZW5ndGggMjcwNDIvU3Vi dHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9 ... bg0KMDAwMDM2NjI5NSAwMDAwMCBuDQowMDAwMzk1NDY2IDAwMDAwIG4NCjAwMDA0MTk5MjggMDAw MDAgbg0KdHJhaWxlcg08PC9TaXplIDQxL1Jvb3QgMSAwIFIvSW5mbyA0MCAwIFIvSURbPEM3MjlE QzVEMUYwODQzNDA4NEY0QTlBNEJBQTE4RjhCPjxDMjU2RDIxQjA5Q0Y0MjQ4QTA5REIzRDgxNjQw NkMzMT5dPj4Nc3RhcnR4cmVmDTQyMDEyMQ0lJUVPRg0= ------=_Part_0_869724450.1481019442425-- 

I tried the following, but it gives an empty result:

  $url="1.2.3.4"; $data = array('name' => basename($filePath), 'file' => '@' . realpath($filePath)); $data = array('file' => '@' . realpath($filePath)); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 300); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-type: multipart/related' )); $result = curl_exec($ch); $info = curl_getinfo($ch); // curl_close($ch); if ($result === false || $info['http_code'] != 200) { $output = "No cURL data returned for $url [" . $info['http_code'] . "]"; if (curl_error($ch)) $output .= "\n" . curl_error($ch); } else { // 'OK' status; format $output data if necessary here: echo 'succes'; } 

Maybe this is the best approach for creating full content using curl-options? Instead of creating content “manually” until ...

Thanks in advance!

+6
source share
1 answer

per fooobar.com/questions/1012950 / ... , libcurl does not have a built-in mechanism for coding requests like multipart / related (and this guy is the author and the main curl developer, so he would know), that is, you have to code the body yourself.

I don’t have access to the server for testing, but I think it will look something like this:

 <?php declare(strict_types = 1); $files = array ( new MultipartRelatedFile ( 'foo.php' ), new MultipartRelatedFile ( 'bar.php' ), new MultipartRelatedFile ( 'baz.php' ) ); $body = generateMultipartRelatedBody ( $files, $boundary ); $ch = curl_init (); curl_setopt_array ( $ch, array ( CURLOPT_HTTPHEADER => array ( 'Content-Type: multipart/related; boundary="' . $boundary.'"' ), CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_URL => 'http://127.0.0.1:9999' ) ); curl_exec ( $ch ); class MultipartRelatedFile { public $path; public $postname; public $content_type; function __construct(string $path) { if (! is_readable ( $path )) { throw new \Exception ( 'file is not readable!' ); } $this->path = $path; $this->content_type = mime_content_type ( $path ); $this->postname = basename ( $path ); } } /** * generate http body for a multipart/related POST request * * @param MultipartRelatedFile[] $files * @param string|null $boundary */ function generateMultipartRelatedBody(array $files, string &$boundary = NULL): string { if (null === $boundary) { $boundary = bin2hex ( random_bytes ( 10 ) ); } $n = "\r\n"; $body = ''; foreach ( $files as $file ) { $body .= $boundary . $n; $body .= 'Content-Disposition: attachment; filename=' . $file->postname . $n; $body .= "Content-Type: " . $file->content_type . $n; $body .= $n . file_get_contents ( $file->path ) . $n; } $body .= $boundary . '--'; return $body; } 

warning: not making sure that it looks correct-ish on the netcat server, this is unverified code. I would also like to note that the specifications do not mention the Content-Length header and imply that Content-Id is optional if the file name is provided in some other way (for example, in Content-Disposition ) AND the start header is not used. anyway specifications can be found here: https://tools.ietf.org/html/rfc2112

edit: forgot to add quotation marks (") around the border of the original Content-Type header, fixed.

+2
source

Source: https://habr.com/ru/post/1012947/


All Articles