PHP导出大量数据(csv)

如果想一次性导出大量数据如几十万,上百万,往往会出现两个问题:

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);//写入第一行数据
            }  
        } 

    }

不明白的可以留言

你可能感兴趣的:(php)