Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

meeshka's avatar

Dynamic content for download (Content-Disposition the laravel way...)

Hi,

I am working on a CSV download feature for a project. I finally got it working with the following (relies directly on Symfony Components (http://symfony.com/doc/current/components/http_foundation.html#serving-files)). When I tried replacing $response = new Response($fileContent); with $response = response() it didn't work. Without, that it works fine within a controller method. I'm trying to make/keep it as Laravel as possible without requiring to import Symfony components directly.

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

$fileContent = ...; // the generated file content
$response = new Response($fileContent);

$disposition = $response->headers->makeDisposition(
    ResponseHeaderBag::DISPOSITION_ATTACHMENT,
    'foo.pdf'
);

return $response->headers->set('Content-Disposition', $disposition);
0 likes
14 replies
pmall's avatar

Here you're not streaming the file. The whole file is generated and stored in memory as you have a $fileContent variable.

The only way to stream a csv is to use Symfony\Component\HttpFoundation\StreamedResponse with a callback generating the content.

1 like
meeshka's avatar

@pmall Good catch. I changed the title. Basically, wanted to confirm if there was any way to directly use laravel's Response (via helper response()) to achieve it. I understand, Laravel uses HttpFoundation.

pmall's avatar

it didn't work

Be more specific please :)

meeshka's avatar

@pmall will try to explain...

Isn't Laravel's Response extended from Symfony\Component\HttpFoundation\Response? So, why can't I do this

$disposition = response()->headers->makeDisposition(
    ResponseHeaderBag::DISPOSITION_ATTACHMENT,
    'foo.pdf'
);

return response()->headers->set('Content-Disposition', $disposition);

instead of

$disposition = $response->headers->makeDisposition(
    ResponseHeaderBag::DISPOSITION_ATTACHMENT,
    'foo.pdf'
);

return $response->headers->set('Content-Disposition', $disposition);
pmall's avatar

But what is the bug when you do this?

meeshka's avatar

@pmall I get this

Undefined property: Illuminate\Routing\ResponseFactory::$headers

and here is what controller method has

        $disposition = response()->headers->makeDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            'Gateways_CSV_' . date('YmdHis')
        );

        

        return response('File Contents')->header('Content-Disposition', $disposition);
meeshka's avatar

Ok. I get this working finally.

        $response = Response::make('File contents', 200);
        $response->header('Content-Type', 'application/json');
        $response->header('Content-Disposition', 'attachment; filename="myfile.txt"');
        return $response;

and

        $response = response('File contents', 200);
        $response->header('Content-Type', 'application/json');
        $response->header('Content-Disposition', 'attachment; filename="myfile.txt"');
        return $response;

both work for me. I'm sure I can reduce it to 1 line or even create a quick shortcut method somewhere. I like using helpers like response() as I feel they work for more versions to come :)

pmall's avatar
pmall
Best Answer
Level 56

I'm sure I can reduce it to 1 line or even create a quick shortcut method somewhere.

Third parameter of the response object/helper is an associative array o headers :

$response = response('File contents', 200, [
    'Content-Type' => 'application/json',
    'Content-Disposition' => 'attachment; filename="myfile.txt"',
]);
7 likes
meeshka's avatar

@pmall Now, that's the perfect answer :) Because, that's what I meant by "the Laravel way" ;)

Btw, where can I find these details API docs doesn't have this. Does it?

Also, what if the content generation failed and want to notify the user about it? Send 404 instead of 200?

dylanh's avatar
        return response()->download(
                storage_path($fileAttachment->path),
                $fileAttachment->name,
                [],
                'inline'
            );

If anyone stumbles upon this and is looking for another option, I settled on the above.

It allows you to do an inline view of the file, but still set the file name explicitly.

If you want it to go as a download, change "inline" to "attachment".

4 likes
boneill81's avatar

@dylanh Absolute legend, this is exactly 100% what I needed to solve my issue today. A+

1 like
alfrednutile's avatar

I ended up with to help decrypt some data before downloading it to user:

return response()->streamDownload(
                function () use ($passenger) {
                    $path = sprintf('passengers/%d/%s',
                        $passenger->id,
                        $passenger->profile->image);

                    $content = Storage::disk('s3')->get($path);

                    echo decrypt($content);
                },
                'passport.jpg',
                [
                    'X-Vapor-Base64-Encode' => 'True',
                ],
                'attachment'
            );
1 like

Please or to participate in this conversation.