Laravel 5.8 StreamDownload File Attachment Redirecting Instead of Downloading

1.5k Views Asked by At

I think I'm making a mistake that I'm not seeing in my Laravel controller. I have JSON data that I want to spit into a CSV, and then download that file in the browser.

I'm submitting a form with hidden inputs to POST data to the server, running queries for the data, then using fputcsv() to create a file stream callback, and returning a Laravel Stream Response.

I want to stream download it, and not write a file on the server to download. I'm aware of tricks to use ajax or blobs to do this instead, but I feel like a form POST to download a CSV file should be pretty simple and I'm doing something stupid. If I return just the raw data, the CSV strings look correct, so I don't think it's an issue with the processing or data itself.

ExportController

protected function csvFileHeaders($filename) {
    return array(
        "Content-type" => "text/csv",
        "Content-Disposition" => "attachment; filename=". $filename,
        "Pragma" => "no-cache",
        "Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
        "Expires" => "0"
    );
}

public function __invoke() {

$filename = 'report_'.$endpoint.'_'.date('d-m-Y-H:i:s').'.csv';
$headers = $this->csvFileHeaders($filename);

$data    = $rows->toArray();
$columns = collect(request()->input('params.columns'))->map(function($c) {
    return $c['label'];
})->toArray();
$fields = collect(request()->input('params.columns'))->map(function($c) {
    return $c['value'];
})->toArray();

$callback = function() use ($data, $fields, $columns) {
    $file = fopen('php://output', 'rw');
    fputcsv($file, $columns);

    foreach($data as $d) {
        // MAP values to columns OR empty strings if value is not present
        fputcsv($file, collect($fields)->map(function($f) use ($d) {
            if ( array_key_exists($f, $d) ) { return $d[$f]; }
            return '';
        })->toArray());
    }

    fclose($file);
};


return response()->stream($callback, $filename, $headers);

}

Frontend React POST Form

<form id="report-export-form" action={'/report/export'} method="POST">
    <div dangerouslySetInnerHTML={{ __html: csrf_token }} />
    {/* BECOMES */}
    {/* <div><input type="hidden" name="_token" value="...token value..."></div> */}

    <input type="hidden" name="mode" value={form.mode)} />
    <input type="hidden" name="endpoint" value={endpoint} />

    <button className="btn btn-success" type="submit">
        Export to CSV
    </button>
</form>

What happens instead of a file download is the page redirects (refreshes to the original page) and no file is downloaded. Does anyone see what mistake I'm making?

1

There are 1 best solutions below

0
On

Turns out there was a Validator rule check that was failing silently, causing the redirect.

Fixing the validation and making sure to set the Content-Type and Content-Disposition headers successfully downloaded the attachment.

Moral of story -- simple first and don't sleep deprive yourself.