Impirator
Impirator

Reputation: 1442

How to download a temporary file

I'm trying to create a short PHP script that takes a JSON string, converts it to CSV format (using fputcsv), and makes that CSV available as a downloaded .csv file. My thought was to use tmpfile() to not worry about cronjobs or running out of disk space, but I can't seem to make the magic happen.

Here's my attempt, which is a converted example from the PHP docs of readfile:

$tmp = tmpfile();
$jsonArray = json_decode( $_POST['json'] );
fputcsv($tmp, $jsonArray);

header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename='.basename($tmp));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($tmp));

ob_clean();
flush();
readfile($tmp);

I feel like the Content-Disposition and Content-Transfer-Encoding headers are wrong, but I'm not sure how to fix them. For example, basename($tmp) doesn't seem to return anything, and I'm not sure text/csv transfers as binary encoding. Likewise, echo filesize($tmp) is blank as well. Is there a way to do what I'm attempting here?

[Edit]

**Below is the working code I wrote as a result of the accepted answer:*

$jsonArray = json_decode( $_POST['json'], true );
$tmpName = tempnam(sys_get_temp_dir(), 'data');
$file = fopen($tmpName, 'w');

fputcsv($file, $jsonArray);
fclose($file);

header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=data.csv');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($tmpName));

ob_clean();
flush();
readfile($tmpName);

unlink($tmpName);

Upvotes: 27

Views: 23293

Answers (4)

Ivo
Ivo

Reputation: 5420

Note that tmpfile() returns a file handle, but all the functions you have need a file path (i.e a string), not a handle - notably basename, filesize, and readfile. So none of those function calls will work correctly. Also basename won't return a file extension either. Just call it whatever you want, i.e

'Content-Disposition: attachment; filename=data.csv'

As @Joshua Burns also says, make sure you're passing in an array to fputcsv or use the assoc parameter.

Upvotes: 8

Jocelyn
Jocelyn

Reputation: 11393

One problem is that tmpfile() returns a file handle, while filesize takes a filename as parameter.
The following line:

header('Content-Length: ' . filesize($tmp));

probably gets evaluated as:

header('Content-Length: 0');

Check that all the calls to header() are executed before any output happens.

Upvotes: 0

Anton
Anton

Reputation: 1061

Firstly, check this string out, i would change it to:

header('Content-Disposition: attachment; filename="'.basename($tmp).'"');

i had the problem with it once, with browser compatibility :)

Upvotes: 0

Joshua Burns
Joshua Burns

Reputation: 8572

json_decode() by default translates elements into objects rather than arrays. fputcsv() expects the data passed to be an array.

I'd recommend changing:

$jsonArray = json_decode( $_POST['json'] );

To:

$jsonArray = json_decode( $_POST['json'], True );

And see if that doesn't fix your problem.

When attempting to tackle problems such as these I'd highly recommend enabling display_errors and setting error_reporting to E_ALL to see if there some sort of error you are missing out on:

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

// Rest of your code here

Upvotes: 3

Related Questions