Search code examples
phpmacossafarifopenfputcsv

Safari (or Apple OS) adding .txt to .csv download


I am using PHP to create a simple .csv from database entries in Laravel, here's a little sample of the code:

$file = fopen(storage_path('file.csv'), 'w');
//$printer = array from database     
foreach ($printer as $row) {
    fputcsv($file, $row);
}
fclose($file);
//$formname = Object values
$headers = array(
      'Content-Type: text/csv',
      'Content-Length: ' . filesize(storage_path('file.csv')),
      'Content-Disposition: attachment; filename="'.$formname->name.'.csv"'
);
return Response::download(storage_path('file.csv'), $formname->name.'.csv', $headers);

All pretty standard and work's fine for me, but my Client is using OS X 10.9.3 & Safari 7.0.4 and each download is given a .txt extension and I can't find a way to stop it from either server or client side. (so it downloads as file.csv.txt)

I have searched Google but can only find other users with the problem - no solution, the code above is already edited using suggestions found on Stackoverflow: 1. laravel adding .txt on the downloaded file 2. How to use the CSV MIME-type?

Can anyone tell me where this problem arises and how to fix it? And is there anywhere I can test the download as I only have Windows and the testing websites I use only provide visual renders.

Also: The client has assured me he can download .csv's from other sources without this problem


Solution

  • As I can see Symfony iterates through the headers as key-value pairs:

    public function __construct(array $headers = array())
    {
        foreach ($headers as $key => $values) {
            $this->set($key, $values);
        }
    }
    

    And then sets them this way:

    if (true === $replace || !isset($this->headers[$key])) {
        $this->headers[$key] = $values;
    } else {
        $this->headers[$key] = array_merge($this->headers[$key], $values);
    }
    

    So if you only have values in the $headers array with numeric indexes, the result might be unexpected. Try to change your code to this:

    $headers = array(
        'Content-Type' => 'text/csv',
        'Content-Length' => filesize(storage_path('file.csv')),
        'Content-Disposition' => 'attachment; filename="'.$formname->name.'.csv"'
    );