2

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!

enriqg9
  • 1,455
  • 1
  • 21
  • 40
  • What is the value of `$file_info->buffer($file)`? – maiorano84 May 25 '16 at 01:19
  • @maiorano84 I am going to be working mostly with PDF, Doc/x, and Dwg. Depending on the file that gets streamed i get `"application/pdf"`, `"application/vnd.openxmlformats-officedocument.wordprocessingml.document"` or `"image/vnd.dwg"` from `$file_info->buffer($file)` – enriqg9 May 25 '16 at 01:22
  • Just for giggles, try setting your headers to the following: `[ 'Content-Type' => 'application/octet-stream', 'Content-Disposition' => 'attachment; filename="' . $document->name . '"' ]` – maiorano84 May 25 '16 at 01:28
  • @maiorano84 I still get the following with a Word document: Word found unreadable content in file.docx. Do you want to recover the contents of this document? If you trust the source of this document, click Yes. – enriqg9 May 25 '16 at 01:30
  • Since I've never used the Storage layer before, it's hard for me to say where the disconnect might be. That said, the Laravel response object does have a download method. See if [this answer](http://stackoverflow.com/questions/20415444/download-files-in-laravel-using-responsedownload) might help in streamlining the process for you a little. – maiorano84 May 25 '16 at 01:39
  • @maiorano84 It is throwing an exception :/ "ErrorException in File.php line 36: is_file() expects parameter 1 to be a valid path, string given". I am reading [this answer](http://stackoverflow.com/questions/6003528/forcing-download-from-s3-amazon-servers?rq=1) that may be I have to set the headers on upload. – enriqg9 May 25 '16 at 01:44

1 Answers1

4

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 . '"'
    ]);
}
enriqg9
  • 1,455
  • 1
  • 21
  • 40