phpexcle大数据量导出

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/grn11/article/details/88932768

引导关注商务学习动图

最近接到一个需求,通过选择的时间段导出对应的订单数据到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 <$pages ; $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 <= $pages; $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();
}

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

更多信息交流,敬请关注号
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/grn11/article/details/88932768