zoukankan      html  css  js  c++  java
  • PHP实时生成并下载超大数据量的EXCEL文件

    最近接到一个需求,通过选择的时间段导出对应的用户访问日志到excel中, 由于用户量较大,经常会有导出50万加数据的情况。而常用的PHPexcel包需要把所有数据拿到后才能生成excel, 在面对生成超大数据量的excel文件时这显然是会造成内存溢出的,所以考虑使用让PHP边写入输出流边让浏览器下载的形式来完成需求。

    我们通过如下的方式写入PHP输出流

    $fp = fopen('php://output', 'a');
    fputs($fp, 'strings');
    ....
    ....
    fclose($fp)

    php://output是一个可写的输出流,允许程序像操作文件一样将输出写入到输出流中,PHP会把输出流中的内容发送给web服务器并返回给发起请求的浏览器

    另外由于excel数据是从数据库里逐步读出然后写入输出流的所以需要将PHP的执行时间设长一点(默认30秒)set_time_limit(0)不对PHP执行时间做限制。

    注:以下代码只是阐明思路和步骤,请根据自己的需求填充对应的业务代码!

    set_time_limit(30);
    $filename = date('YmdHis',time());
    header('Content-Type:application/vnd.ms-execl');
    header('Content-Disposition:attachment;filename='.$filename.'.csv');
    
    //打开php标准输出流,以写入追加的方式打开
    $fp = fopen('php://output', 'a');
    
    //设置标题
    $title = array('ID','Areaid','Userid','Sellerid','Type','OrderNo','Create_Time','Order_transaction','Mpayintegral','Order_amount','Currency','Total_amount','Payment_time','Bank_charges','Bank_settlement_amount');
    
    foreach ($title as $key => $item) {
        $title[$key] = iconv('UTF-8', 'GBK', $item);
    }
    
    //将标题写到标准输出中
    fputcsv($fp, $title);
    
    
    //用fputcsv导出数据
    $count = Order::where('id','>',0)->where('id','<',1200)->count();//从数据库获取总量
    $perSize = 1000;//每次查询的条数
    $step = ceil($count/$perSize);
    
    $lastId  = 0;
    for ($i=1; $i <=$step ; ++$i) { 
        $start= ($i-1) * $perSize;
    
        $data = Order::select('id','areaid','userid','sellerid','type','order_no','create_time','order_transaction','mpayintegral','order_amount','currency','total_amount','payment_time','bank_charges','bank_settlement_amount')->where('id','>',$lastId)->orderBy('id','asc')->limit($perSize)->get();
    
        if($data){
            foreach ($data as $key => $value) {
                $row[] = $value->id;
                $row[] = $value->areaid;
                $row[] = $value->userid;
                $row[] = $value->sellerid;
                $row[] = $value->type;
                $row[] = $value->order_no."	";
                $row[] = $value->create_time."	";
                $row[] = $value->order_transaction."	";
                $row[] = $value->mpayintegral;
                $row[] = $value->order_amount;
                $row[] = $value->currency;
                $row[] = $value->total_amount;
                $row[] = $value->payment_time."	";
                $row[] = $value->bank_charges;
                $row[] = $value->bank_settlement_amount;
                fputcsv($fp, $row);
                unset($row);
                $lastId = $value->id;
            }
            //刷新输出缓冲到浏览器
            //必须同时使用 ob_flush() 和flush() 函数来刷新输出缓冲。
            ob_flush();
            flush();
            unset($data);//释放变量的内存
        }
    }
    fclose($fp);

    因为逐步写入EXCEL的数据实际上来自Mysql的分页查询,语法是LIMIT offset, num 不过随着offset越来越大Mysql在每次分页查询时需要跳过的行数就越多,这会严重影响Mysql查询的效率(包括MongoDB这样的NoSQL也是不建议skip掉多条来取结果集),所以我采用LastId的方式来做分页查询。

    参考:https://www.cnblogs.com/lalalagq/p/9963799.html

  • 相关阅读:
    js命名空间
    window安装node.js
    JS添加可信站点、修改ActiveX安全设置,禁用弹出窗口阻止程序的方法
    Javascript 操作select控件大全(新增、修改、删除、选中、清空、判断存在等)
    DIV+CSS两种盒子模型
    table中的tbody标签
    兼容获取元素的样式属性值
    Arch linux 使用心得
    763. 划分字母区间
    <Leetcode>93. 复原地址
  • 原文地址:https://www.cnblogs.com/clubs/p/13544863.html
Copyright © 2011-2022 走看看