Let's say you want to make a file available for download in your app. This seems pretty simple at first glance, but I'm going to layer on some constraints to make it more interesting.
First, this file is something your browser knows how to display, like a PDF or an image. If you just create a link to it, the browser will display it instead of downloading it.
Maybe you're next thought is to add the download
attribute to the link.
Great idea!
Except that only works for files on the same domain as your app, and this file is coming from a private S3 bucket using the temporaryUrl
method from Laravel's storage API.
Digging into that method, you will see that you can pass an array of options, which will get passed along to the underlying S3 client.
If we look at the S3 docs for how to fetch an object, it lists some request parameters that let you control how it constructs it's HTTP response.
There are two headers in particular that will solve our problem.
Let's look at some code, and then I'll explain how it works:
Storage::disk('s3')->temporaryUrl(
'your-object-key',
Carbon::now()->addHour(),
[
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => "attachment; filename=some-file-name"
]
);
The first parameter tells S3 to set a Content-Type
header on the download response which will override the content type of the file to something the browser will not try to display.
The second parameter tells S3 to set a Content-Disposition
header on the response, which will tell the browser to treat this response as an attachment with the desired file name, triggering the browser to download it.
It's important to note that these headers will only work over an HTTPS connection. This won't be a problem if you're using a cloud storage provider like S3, but it may be an issue for local development if you're not set up for it.
Here to help,
Joel
P.S. Would you like two seasoned Laravel experts to review your application code and architecture? Let's set up a time to talk.