PHPexcle大数据量导出

最近接到一个需求,通过选择的时间段导出对应的订单数据到excel中, 由于数据量较大,经常会有导出500K+数据的情况。平常的导出用的PHPexcle,将数据一次性读取到内存中再一次性写入文件,而在面对生成超大数据量的excel文件时这显然是会造成内存溢出的。

这时就需要循环批量写入excle导出。

过程中可能遇到的问题:

1.超时:Maximum execution time of 30 seconds exceeded

解决:在文件开头加上 set_time_limit(0);即php执行时间不受限制。

2.内存溢出:Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)

解决:

2.1 可在ini文件设置;设置memory_limit,将内存设置加大,

2.2 在执行代码前加上 ini_set("memory_limit", "1024M");

2.3 每次读取的数据量减少,增加循环的次数;

3.导出的excle数字过长展示成科学计数

解决:

3.1 在要写入的数字数据,后面加个 “\t”;

3.2 或是数字转换为字符串格式

上代码(生成excle链接导出数据):

public function downBigExcle()

{

set_time_limit(0);

#自定义导出地址+文件名

$pathName = time().'.csv';

#计算要导出总数据条数

$totalNums = Db::table('t_excle')->count('id');

#设置表头标题

$headerTitleArr = ['xx','xxx'];

#CSV的Excel支持GBK编码,一定要转换,否则乱码

foreach ($headerTitleArr as $i => $v) {

$headerTitleArr[$i] = iconv('utf-8', 'gbk', $v);

}

#每次查询的条数

$pageSize = 5000;

#分批导的次数

$pages = ceil($totalNums / $pageSize);

#打开文件流

$fp = fopen($pathName, 'a');

#将数据格式化为CSV格式并写入到文件流中

fputcsv($fp, $headerTitleArr);

#主体内容

$paegNum = 0;

for ($i=0; $i

$data = Db::table('t_excle')->limit($paegNum,$pageSize)->field('*')->select();

foreach ($data as $fields) {

foreach ($fields as $key => &$v) {

// CSV的Excel支持GBK编码,一定要转换,否则乱码

// 加"\t"防止数字导出时变成科学计数

$v = iconv('utf-8', 'gbk', $v."\t");

}

fputcsv($fp, $fields);

//这边可记录下数据的最后一次的位置,便于查看排错定位。

}

unset($data);//释放变量的内存

$paegNum += $pageSize;

}

fclose($fp);

#输出下载链接,url为下载的域名

return $url.$pathName;

}

对于一个请求来说PHP是单线程的,大数据量的导出又是最耗时的,搞不好其他请求就阻塞了。

小数据量的话,可以直接浏览器输出;

大数据量的话,可以生成excle下载链接;推荐使用swoole异步框架处理请求。

1.当客服在后台系统点击下载后,可先返回个提示或是生成一条下载记录;

2.在后台异步处理excle导出操作;

3.客服主动刷新页面查看是否有下载链接生成。

再上代码(生成excle直接浏览器输出):

public function downOrderData($timeStart, $timeEnd)

{

set_time_limit(0);

$columns = [

'序号ID', '姓名', '电话', ......

];

$csvFileName = '订单数据' . $timeStart .'_'. $timeEnd . '.xlsx';

//设置好告诉浏览器要下载excel文件的headers

header('Content-Description: File Transfer');

header('Content-Type: application/vnd.ms-excel');

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

header('Expires: 0');

header('Cache-Control: must-revalidate');

header('Pragma: public');

$fp = fopen('php://output', 'a');//打开output流

mb_convert_variables('GBK', 'UTF-8', $columns);

fputcsv($fp, $columns);//将数据格式化为CSV格式并写入到output流中

$accessNum = '100000'//从数据库获取总量,假设是十万

$perSize = 1000;//每次查询的条数

$pages = ceil($accessNum / $perSize);

$lastId = 0;

for($i = 1; $i

$data = Db::table('t_excle')->limit($lastId ,$pageSize)->field('*')->select();

foreach($data as $value) {

mb_convert_variables('GBK', 'UTF-8', $value);

fputcsv($fp, $value);

$lastId = $value['id'];

}

unset($data);//释放变量的内存

//刷新输出缓冲到浏览器

ob_flush();

flush();//必须同时使用 ob_flush() 和flush() 函数来刷新输出缓冲。

}

fclose($fp);

exit();

}

PS:更多信息交流,请关照:倒影Amoy

活着就是为了改变世界,难道还有其他原因吗?----乔布斯

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20190331A0BCX900?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券