I saw a lot of codes that import millions of data into CSV files on the Internet. I tried them on my own. M ost of them will cause memory overflow due to too large data. I saw the use of PHP generator by chance and finally found a solution (the memory required is very small, about tens of meters is enough, depending on the range of data you get each time). I took THINKPHP5.1 as an example to test. Try as follows:
My environment is LNMP, PHP version is PHP7.2
- Write a Csv.php class and put it into the extend directory, as follows:
<?php namespace csv; use think\Db; class Csv { /** * Export data to CSV file * @param $fileName Generated csv filename * @param array $title csv The first line of the file displays the title * @param array $showKeys csv Field name in the table displayed corresponding to the file * @param string $table Data table for query * @param string $pk Auto primary key of data table, this field must exist * @param array $where query criteria */ public function putCsv($fileName, $title = [], $showKeys = [], $table = '', $pk = 'id', $where = []) { header('Content-Type: application/vnd.ms-excel');//Set content type to Excel header('Content-Disposition: attachment;filename='.$fileName );//Download File header('Cache-Control: max-age=0');//Indicates that you will not visit the server again within 0 seconds after visiting this web page //Open a file or URL. PHP: / / output is a write only data stream. It allows you to write to the output buffer in the same way as echo and print. A: open in write mode, and point the file pointer to the end of the file. If the file does not exist, try to create it. $file = fopen('php://output',"a"); $limit =1000; $calc = 0; foreach ($title as $v){ $tit[] = iconv('UTF-8', 'GB2312//IGNORE',$v); / / transcode } //Format the line as CSV and write it to an open file. (title) fputcsv($file,$tit); //Number of cycles per time $step = 10000; $j = $step; //Calculate total quantity if (!empty($where)) { $lastIdValue = Db::name($table)->where($where)->order($pk . ' DESC')->limit(1)->value($pk); } else { $lastIdValue = Db::name($table)->order($pk . ' DESC')->limit(1)->value($pk); } for ($i = 1;$i < $lastIdValue;$i = $i + $step) { //Get the generator object returned by the generator here if (!empty($where)) { $cursor = Db::name($table)->where($pk, 'between', [$i, $j])->where($where)->cursor(); } else { $cursor = Db::name($table)->where($pk, 'between', [$i, $j])->cursor(); } foreach ($cursor as $v){ $calc++; //-------Core!!! Clear cache, write data on cache to file-------- if($limit == $calc){ ob_flush();//Take out the existing contents of the output cache, and the contents of the buffer will be discarded after the call of ob_flush(). flush(); //The content to be output is sent immediately. $calc = 0; } //-------Core-------- $arr = []; foreach ($showKeys as $key) { if (isset($v[$key])) { $arr[] = $v[$key]; } } foreach($arr as $t){ $tarr[] = iconv('UTF-8', 'GB2312//IGNORE',$t); } fputcsv($file,$tarr);//Format the line as CSV and write it to an open file. (content) unset($tarr);//Destroy the specified variable } $j = $j + $step; } //unset($list); / / destroy the specified variable fclose($file);//Close open file } }
2, the call class is exported to the CSV file in the controller.
public function testPutCsv() { set_time_limit(0); $fileName = 'test.csv'; $title = ['url address', 'Adding time', 'state']; $showKeys = ['url', 'add_time', 'status']; $where = [ 'status' => 0 ]; $csv = new Csv(); $csv->putCsv($fileName, $title, $showKeys, 'use_qrcode', 'id', $where); }