phpsymfonysymfony-http-client

How do I upload a file from a string, without saving it to disk, with Symfony Http Client?


I'm trying to upload a file to an API, that requires a multipart form upload.

I don't want to store the file locally (I read it from an S3 bucket using flysystem) but I can'get it to work (always get a 422 error).

When I store the file locally first, it works:

// $filecontent is data from Flysystem
$fileContent = $this->documentStorage->read('remote filename.pdf');

file_put_contents(__DIR__.'/test.pdf', $fileContent);
$fileHandle = fopen(__DIR__.'/test.pdf', 'rb');

$params = [
    'body' => [
        'bestand' => $fileHandle
     ],
];

$response = $this->createClient()->request('POST', $url, $params);

I tried skipping the local save with a php://memory and php://temp stream, but that won't work:

$stream = fopen('php://temp', 'rb+');
fwrite($stream, $fileContent);
rewind($stream);

$params = [
     'body' => [
         'bestand' => $stream
     ],
  ];

$response = $this->createClient()->request('POST', $url, $params);

Tried manually making a multipart form, also doesn't work:

$dp = new DataPart($fileContent);
$formData = new FormDataPart(['bestand' => $dp]);
$preparedHeaders = $formData->getPreparedHeaders();

$params = [
     'body' => $formData->bodyToString(),
     'headers' => [
         trim($preparedHeaders->toString())
    ],
];

$response = $this->createClient()->request('POST', $url, $params);

Solution

  • In a project I have, I use this approach, taking advantage of the "data" wrapper:

    
    $fileName = 'some_random_file.mp3';
    
    $fileContents = file_get_contents($fileName);
    
    $fileHandle = fopen('data://text/plain;base64,' . base64_encode($fileContents), 'rb+');
    stream_context_set_option($fileHandle, 'http', 'filename', $fileName);
    stream_context_set_option($fileHandle, 'http', 'content_type', 'audio/mpeg');
    
    $response = $this->httpClient
            ->request('POST', 'http://localhost:8000/upload/file', 
            [
              'headers' => [
                 'Content-Type' => 'multipart/form-data'
               ],
              'body' => [
                'file' => $fileHandle,
            ],
    ]);
    

    The docs mention how to set the name for a case like this.