开发中经常遇到需要从数据库导出大量数据的问题,导出excel需要占用太多内存,最终回导致内存溢出而失败。csv是更好的选择。同时可以使用php5.5之后赋予的新功能——yield(生成器)来优化性能,具体可以看鸟哥博客http://www.laruence.com/tag/yield
/**
* 分段导出所有用户
*/
public function exportAll()
{
set_time_limit(0);
$count = D('user')->count();
$num = 0;
$f = null;
foreach ($this->getCounts($count,function($fp) use (&$f){
$f = $fp;
}) as $value) {
$num++;
if (1000 === $num) {
ob_flush();
flush();
$num = 0;
}
foreach ($value as $v) {
fputcsv($f,$v);
}
fclose($f);
}
//导出合并后的文件
$this->mergeFile(function($file){
error_reporting(0);
header("Cache-Control: max-age=0");
header("Content-Description: File Transfer");
header('Content-disposition: attachment; filename=songdaozong.csv'); // 文件名
header("Content-Type: application/csv");
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . filesize($file));
readfile($file);//输出文件;
self::clearFile();
});
}
/**
* 获取阶段导出数据
*/
private function getCounts($count,$handle)
{
$pageCount = ceil($count/1000);
for ($i=0; $i < $pageCount; $i++) {
$file = __DIR__ . '/_' . $i . '.csv';
touch($file);
$fp = fopen($file, 'w'); //生成临时文件
if ($handle instanceof \closure) {
$handle($fp);
}
yield D('user')->limit($i*1000,1000)->select();
}
}
/**
* 处理数组格式
*/
private static function makeArray(array $data)
{
$new = [];
foreach ($data as $key => $value) {
$new[] = array_values($value);
}
return $new;
}
/**
* 合并文件
*/
private function mergeFile($handle)
{
$fileList = glob(__DIR__.'/*.csv');
$count = count($fileList);
for ($i=0; $i < $count; $i++) {
if ($i > 0) {
file_put_contents($fileList[0],file_get_contents($fileList[$i]),FILE_APPEND);
}
}
if ($handle instanceof \closure) {
$handle($fileList[0]);
}
}
/**
* 清除文件
*/
private static function clearFile()
{
error_reporting(0);
$fileList = glob(__DIR__.'/*.csv');
foreach ($fileList as $value) {
unlink($value);
}
}