enriqg9 enriqg9 - 6 months ago 405
PHP Question

Laravel file downloaded from AWS S3 with Filesystem gets corrupted

I am uploading files to Amazon S3 with Laravel filesystem. The upload process works great, however, when I download the files get corrupted. I have manually downloaded the files from the S3 bucket and that way the files don't get corrupted, so I figured that the problem is not the upload.

I am uploading the files like this:

/**
* Upload the file to Amazon S3.
*
* @param UploadedFile $file
* @param $path
* @return $this|bool
*/
protected function upload(UploadedFile $file, $path)
{
$this->filename = $path . '/' . time() . '_' . str_replace(' ', '-', $file->getClientOriginalName());

$disk = Storage::cloud();

if ($disk->put($this->filename, fopen($file, 'r+'))) {

$this->save();

return $this;
}

return false;
}


And to download I have tried this:

/**
* @param Document $document
* @return Response
*/
public function download(Document $document)
{
$file = Storage::cloud()->get($document->path);

$file_info = new finfo(FILEINFO_MIME_TYPE);

return response($file, 200)->withHeaders([
'Content-Type' => $file_info->buffer($file),
'Content-Disposition' => 'inline; filename="' . $document->name . '"'
]);
}


And this:

/**
* @param Document $document
* @return Response
*/
public function download(Document $document)
{
$stream = Storage::cloud()->getDriver()->readStream($document->path);

$file = stream_get_contents($stream);

$file_info = new finfo(FILEINFO_MIME_TYPE);

return response($file, 200)->withHeaders([
'Content-Type' => $file_info->buffer($file),
'Content-Disposition' => 'inline; filename="' . $document->name . '"'
]);
}


With both download functions I get the files, however they become corrupted. Any help is appreciated!

Answer

The problem was that the output buffer contained a whitespace. Using ob_end_clean() before returning the response solved the issue, but upon finding a whitespace on a file before the opening <?php tag, there was no need to use ob_end_clean().

Here is the code without using a presigned url:

/**
 * Download document from S3.
 *
 * @param Document $document
 * @return Response
 */
public function download(Document $document)
{
    $s3Client = Storage::cloud()->getAdapter()->getClient();

    $stream = $s3Client->getObject([
        'Bucket' => 'bucket',
        'Key'    => $document->path
    ]);

    return response($stream['Body'], 200)->withHeaders([
        'Content-Type'        => $stream['ContentType'],
        'Content-Length'      => $stream['ContentLength'],
        'Content-Disposition' => 'inline; filename="' . $document->name . '"'
    ]);
}