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

  • 相关阅读:
    LeetCode OJ String to Integer (atoi) 字符串转数字
    HDU 1005 Number Sequence(AC代码)
    HDU 1004 Let the Balloon Rise(AC代码)
    HDU 1003 Max Sum(AC代码)
    012 Integer to Roman 整数转换成罗马数字
    011 Container With Most Water 盛最多水的容器
    010 Regular Expression Matching 正则表达式匹配
    007 Reverse Integer 旋转整数
    006 ZigZag Conversion
    005 Longest Palindromic Substring 最长回文子串
  • 原文地址:https://www.cnblogs.com/clubs/p/13544863.html
Copyright © 2011-2022 走看看