120

I am a novice programmer and I searched a lot about my question but couldn't find a helpful solution or tutorial about this.

My goal is I have a PHP array and the array elements are showing in a list on the page.

I want to add an option, so that if a user wants, he/she can create a CSV file with array elements and download it.

I don't know how to do this. I have searched a lot too. But yet to find any helpful resource.

Please provide me some tutorial or solution or advice to implement it by myself. As I'm a novice please provide easy to implement solutions.

My array looks like:

Array
(
    [0] => Array
        (
            [fs_id] => 4c524d8abfc6ef3b201f489c
            [name] => restaurant
            [lat] => 40.702692
            [lng] => -74.012869
            [address] => new york
            [postalCode] => 
            [city] => NEW YORK
            [state] => ny
            [business_type] => BBQ Joint
            [url] => 
        )

)
Alex Shesterov
  • 26,085
  • 12
  • 82
  • 103
user2302780
  • 1,219
  • 2
  • 9
  • 6

8 Answers8

253

You can use the built in fputcsv() for your arrays to generate correct csv lines from your array, so you will have to loop over and collect the lines, like this:

$f = fopen("tmp.csv", "w");
foreach ($array as $line) {
    fputcsv($f, $line);
}

To make the browsers offer the "Save as" dialog, you will have to send HTTP headers like this (see more about this header in the rfc):

header('Content-Disposition: attachment; filename="filename.csv";');

Putting it all together:

function array_to_csv_download($array, $filename = "export.csv", $delimiter=";") {
    // open raw memory as file so no temp files needed, you might run out of memory though
    $f = fopen('php://memory', 'w'); 
    // loop over the input array
    foreach ($array as $line) { 
        // generate csv lines from the inner arrays
        fputcsv($f, $line, $delimiter); 
    }
    // reset the file pointer to the start of the file
    fseek($f, 0);
    // tell the browser it's going to be a csv file
    header('Content-Type: text/csv');
    // tell the browser we want to save it instead of displaying it
    header('Content-Disposition: attachment; filename="'.$filename.'";');
    // make php send the generated csv lines to the browser
    fpassthru($f);
}

And you can use it like this:

array_to_csv_download(array(
  array(1,2,3,4), // this array is going to be the first row
  array(1,2,3,4)), // this array is going to be the second row
  "numbers.csv"
);

Update:
Instead of the php://memory you can also use the php://output for the file descriptor and do away with the seeking and such:

function array_to_csv_download($array, $filename = "export.csv", $delimiter=";") {
    header('Content-Type: application/csv');
    header('Content-Disposition: attachment; filename="'.$filename.'";');

    // open the "output" stream
    // see http://www.php.net/manual/en/wrappers.php.php#refsect2-wrappers.php-unknown-unknown-unknown-descriptioq
    $f = fopen('php://output', 'w');

    foreach ($array as $line) {
        fputcsv($f, $line, $delimiter);
    }
}   
complex857
  • 20,425
  • 6
  • 51
  • 54
  • 2
    why not `echo` the generated CSV lines directly in the loop? – Alex Shesterov Apr 27 '13 at 20:54
  • 1
    @AlexShesterov, the main reason is that `fputcsv()` doesn't return the generated line, it will need a file descriptor to write to. You could rewind, write, clear the buffer on every iteration though if you are memory constrained. Or just use a real temporary file instead. – complex857 Apr 27 '13 at 21:26
  • 1
    @complex857 how can I add column title for the csv ? – user2302780 Apr 28 '13 at 16:40
  • @user2302780: You just prefix your data with a row of column names. You can use the keys from the data-arrays with something like `$data = array_merge(array(array_keys($data[0])), $data);` – complex857 Apr 28 '13 at 17:30
  • Why writing to a file before outputting? If a function doesn't return the result, use ob_start and ob_get_contents to intercept. – Schien Mar 09 '15 at 19:05
  • @Schien, sometimes you need to perform things like `iconv()` on the whole output and it's easier yo grab it from the `php://memory` (not in the example above). I feel like using the output buffer is hackish, but it might just very well be matter of taste (just add an extra option to the function to control it's behavior). – complex857 Mar 10 '15 at 19:58
  • 2
    With php://output generate this error: fseek() stream does not support seeking, but with php://memory works like a charm! – Jose Rojas Feb 07 '16 at 05:33
  • 1
    @JoseRojas that is to be expected since once you put something over the wire (that what outputting things with php means conceptually) you can't go back on that. To use `php://output` see the second updated code sample. – complex857 Feb 07 '16 at 08:34
  • Is there a way to also include the column headings of the table? – Ben Apr 01 '21 at 07:55
  • @Ben: You should be able to do so with grabbing the keys of the first "line" and sending it to the output first (assuming your array keys are the columns): `fputcsv($f, array_keys(reset($array)), $delimiter);` in front of the `foreach` of the lines – complex857 Apr 01 '21 at 12:58
  • For utf-8 encoding: https://stackoverflow.com/a/71206195/9507402 – Malkhazi Dartsmelidze Feb 21 '22 at 12:20
21

I don't have enough reputation to reply to @complex857 solution. It works great, but I had to add ; at the end of the Content-Disposition header. Without it the browser adds two dashes at the end of the filename (e.g. instead of "export.csv" the file gets saved as "export.csv--"). Probably it tries to sanitize \r\n at the end of the header line.

Correct line should look like this:

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

In case when CSV has UTF-8 chars in it, you have to change the encoding to UTF-8 by changing the Content-Type line:

header('Content-Type: application/csv; charset=UTF-8');

Also, I find it more elegant to use rewind() instead of fseek():

rewind($f);

Thanks for your solution!

Bennett McElwee
  • 24,740
  • 6
  • 54
  • 63
Luxian
  • 676
  • 7
  • 14
  • 2
    I've never experienced the `--` at the end of the filenames myself, however I've updated the answer (why not just submit an edit?) – complex857 Jan 24 '14 at 07:33
20

That is the function that I used for my project, and it works as expected.

function array_csv_download( $array, $filename = "export.csv", $delimiter=";" )
{
    header( 'Content-Type: application/csv' );
    header( 'Content-Disposition: attachment; filename="' . $filename . '";' );

    // clean output buffer
    ob_end_clean();
    
    $handle = fopen( 'php://output', 'w' );

    // use keys as column titles
    fputcsv( $handle, array_keys( $array['0'] ), $delimiter );

    foreach ( $array as $value ) {
        fputcsv( $handle, $value, $delimiter );
    }

    fclose( $handle );

    // flush buffer
    ob_flush();
    
    // use exit to get rid of unexpected output afterward
    exit();
}
Sajidur Rahman
  • 2,764
  • 1
  • 27
  • 26
  • 1
    I had a hard time trying to implement it in wordpress send_headers hook, cos every time it add all current page html to the end of csv file. These answer halp me a lot, but I use it without buffer operations. – Oleg Apanovich Jan 17 '20 at 13:01
  • clean output buffer solved for me the problem that before the usage of these code rows in the beginning and at the end of the formed file were empty rows. – Sharunas Bielskis May 29 '20 at 17:57
18

Try... csv download.

<?php 
mysql_connect('hostname', 'username', 'password');
mysql_select_db('dbname');
$qry = mysql_query("SELECT * FROM tablename");
$data = "";
while($row = mysql_fetch_array($qry)) {
  $data .= $row['field1'].",".$row['field2'].",".$row['field3'].",".$row['field4']."\n";
}

header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="filename.csv"');
echo $data; exit();
?>
Jake N
  • 10,535
  • 11
  • 66
  • 112
Indrajeet Singh
  • 2,958
  • 25
  • 25
  • will break if a field value contains , that's why i prefer tsv (tab is more rare than , and we can replace it or something) – oriadam May 08 '20 at 21:31
  • Simple and easy. Thx –  Jan 18 '21 at 22:15
  • "exit():" - thank you!! I was getting the whole HTML of my page after the data I wanted – DJDave May 06 '21 at 06:45
  • I understand this is slightly irrelevant, but how would I do this with a woocommerce product? So instead of reading from database, it reads from variables already present on the single product page. Example: $product->get_id(); then exports these to a csv on a link click? – Paul M Apr 22 '22 at 14:35
9

Use the below code to convert a php array to CSV

<?php
   $ROW=db_export_data();//Will return a php array
   header("Content-type: application/csv");
   header("Content-Disposition: attachment; filename=test.csv");
   $fp = fopen('php://output', 'w');

   foreach ($ROW as $row) {
        fputcsv($fp, $row);
   }
   fclose($fp);
Kiran Reddy
  • 734
  • 12
  • 28
3

If you're array structure will always be multi-dimensional in that exact fashion, then we can iterate through the elements like such:

$fh = fopen('somefile.csv', 'w') or die('Cannot open the file');

for( $i=0; $i<count($arr); $i++ ){
    $str = implode( ',', $arr[$i] );
    fwrite( $fh, $str );
    fwrite( $fh, "\n" );
}
fclose($fh);

That's one way to do it ... you could do it manually but this way is quicker and easier to understand and read.

Then you would manage your headers something what complex857 is doing to spit out the file. You could then delete the file using unlink() if you no longer needed it, or you could leave it on the server if you wished.

half-fast
  • 302
  • 1
  • 7
1

Update for UTF-8 Encoding

Updating @complex857 's answer

function array_to_csv_download($array, $filename = "export.csv", $delimiter=",") {
    header('Content-Disposition: attachment; filename="'.$filename.'";');
    header('Content-Type: application/csv; charset=UTF-8');

    // open the "output" stream
    $f = fopen('php://output', 'w');
    // Write utf-8 bom to the file
    fputs($f, chr(0xEF) . chr(0xBB) . chr(0xBF));

    foreach ($array as $line) {
        fputcsv($f, $line, $delimiter);
    }
}  

May be a bit ugly code but it works!

Malkhazi Dartsmelidze
  • 4,783
  • 4
  • 16
  • 40
1

This function can generate a downloadable CSV file, you can set up the name and the delimiter without tricky functions (UTF-8 encoding in header function).

/**
 * Array2CSVDownload
 * 
 */
function Array2CSVDownload($array, $filename = "export.csv", $delimiter=";") {

    // force object to be array, sorry i was working with object items
    $keys = array_keys( (array) $array[0] );
    // use keys as column titles
    $data = [];
    array_push($data, implode($delimiter, $keys));
    // working with items
    foreach ($array as $item) {
        $values = array_values((array) $item);
        array_push($data, implode($delimiter, $values)); 
    }
    // flush buffer
    ob_flush();
    // mixing items
    $csvData = join("\n", $data);
    //setup headers to download the file
    header('Content-Disposition: attachment; filename="'.$filename.'";');
    //setup utf8 encoding
    header('Content-Type: application/csv; charset=UTF-8');
    // showing the results
    die($csvData);

}   
Watash1
  • 320
  • 4
  • 7