如果想一次性导出大量数据如几十万,上百万,往往会出现两个问题:
1.脚本执行超时
2.php内存不够
第一个问题很好解决:set_time_limit(0);设置为0时,表示不限制脚本的时间。但是不建议这样设置,一般会给个时间,比如一小时,半小时
第二问题你可以设置php的内存,但是这种方式是不可取的,试想一下如果执行这个脚本占那么大内存,那么其他应用就会受影响。
这里提供一种方法,就是分批导出数据。这里我使用thinkphp5开发的,把它写成通用了。看下面例子
html:
a href="javascript:;" data-url="{:url('export')}" class="btn btn-primary export">导出表格
js:这段js是写在公共文件的
//导出表格
$(document).on('click','.export',function(){
var url = $(this).data('url'); //地址
var query = $('.form-inline').find('input').serialize();//表单序列化
query += '&'+$('.form-inline').find('select').serialize();//表单序列化
query = query.replace(/(&|^)(\w*?\d*?\-*?_*?)*?=?((?=&)|(?=$))/g,'');
query = query.replace(/^&/g,'');
if( url.indexOf('?')>0 ){
url += '&' + query;
}else{
url += '?' + query;
}
window.open(url,'_blank'); //新窗口打开
})
PHP代码
//导出表格-任务统计
public function export()
{
//这里是筛选的条件,代码就不贴突出来了
list($map,$sort) = $this->_map();
//这段可忽略,每个人写法不一样
$filter = $map;
if(!empty($filter)){
$field = array_column($filter, 0);
if(!in_array('t1.delete_time', $field)) $filter[] = ['t1.delete_time','=',0];
}else{
$filter[] = ['t1.delete_time','=',0];
}
//这里是返回一个查询对象
$query = Db::name('share_task')->alias('t1')
->leftJoin('hd_share_render t3','t1.render_id=t3.id')
->leftJoin('hd_user t2','t1.receiver_id=t2.id')
->where($filter)
->field('t1.*,t2.truename as receiver,t3.designer_name');
//键值一定要跟field中的值对应上
$head = [
'task_id'=>'任务ID',
'brand'=>'品牌',
'designer_name'=>'发布者',
'receiver'=>'接单者',
'status'=>'任务状态',
'create_time'=>'发布时间',
'finish_time'=>'完成时间',
];
$filename = date('Ymd',time()).'.csv';
//调用写在BusinessApi这个类里面的公共方法,这个方法可以在任意地方调用
BusinessApi::putCsv($query,$head,$filename);
}
BusinessApi类中的putCsv
/**
* 导出csv
* @param object $query sql对象
* @param array $head 头部信息
* @param string $filename 文件名
* @param string $group 分组
* @return
*/
static public function putCsv($query,$head,$filename,$group='')
{
set_time_limit(1800);//制脚本执行时间为半个小时
// 输出Excel文件头,可把user.csv换成你要的文件名
header('Content-Type: application/vnd.ms-excel;charset=utf-8');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');
//总条数
$total = $query->count();
$page_size = 10000;//每次只从数据库取10000条以防变量缓存太大
// 每隔$limit行,刷新一下输出buffer,不要太大,也不要太小
$limit = 10000;
// buffer计数器
$cnt = 0;
$fp = fopen('php://output', 'a');
$key = [];
foreach ($head as $i => $v) {
// CSV的Excel支持GBK编码,一定要转换,否则乱码
$head[$i] =iconv("utf-8","gb2312//IGNORE",$v);
array_push($key, $i);
}
fputcsv($fp, $head);
for ($i = 0; $i < ceil($total/$page_size); $i++) {
if($group) $query->group($group);
$lists = $query->limit($i*$page_size,$page_size)->select()->toArray();
foreach ($lists as $k => $v) {
$data = [];
foreach ($key as $vkey) {
//这里的条件判断看情况而定,比如说下面格式化时间,如果数据量很大的话就不要做在这里做判断了,有可能影响php的效率
if($vkey == 'brand'){
$value = config('app.brand')[$v[$vkey]];
}else if($vkey == 'status'){
$value = config('app.share_task_status')[$v[$vkey]];
}else if(strpos($vkey,'_time') > 0){
$value = date('Y-m-d H:i',$v[$vkey]);
}else{
$value = $v[$vkey];
}
$value = iconv("utf-8","gb2312//IGNORE",$value);
array_push($data, $value);
}
$cnt++;
if ($limit == $cnt) {
//刷新一下输出buffer,防止由于数据过多造成问题
ob_flush();
flush();
$cnt = 0;
}
fputcsv($fp, $data);//写入第一行数据
}
}
}
不明白的可以留言