PHP 流式导出excel大量数据

MyAnswer博客

1.Mysql采用分页查询数据较少数据库压力

2.php的数组控制数据大小,防止内存爆满

3.缓冲区的数据满100条数据后调用flush缓存,清空缓冲区,将数发送给前端,分段发送, 如果一次性发送所有数据缓冲区会爆满,网络也可能会发生阻塞情况


实例

<?php
/*导出1000000万条数据实例*/
/*导入类包*/
require('xlsTools.php');

/*实例化类包*/
$xlsTools = new xlsTools();


//定义列名和导出名称以及格式
$xlsTools->start([
    'title'=>['名字','年龄','成绩'],   //列名
    'type'=>'xls',               //导出的excel的类型
    'name'=>'导出'                //导出的excel的文件名
]);

/* 导出100万条(1000000条)示例 */
for ($i=0; $i<1000000; $i++){
    $row = ['名字'=>'张三','年龄'=>10,'成绩'=>100];

    //输出数据,并刷新缓冲区
    $xlsTools->oneData($row);
}

/*结束导出*/
$xlsTools->end();

xlsTools.php类

<?php

/*这个是类包*/

class xlsTools
{

    var $inEncode = 'utf-8';
    var $outEncode = 'gb2312';

    protected $rowCount; //存储已经存在内存中的记录条数


    protected $rowFlushCount; //  一次flush的数据条数

    public function __construct($rowFlushCount = 1000)
    {
        $this->rowFlushCount = $rowFlushCount;
        $this->rowCount = 0;
    }

    public function __destruct()
    {
        // TODO: Implement __destruct() method.
    }

    public function start($param)
    {
        // todo 文件名这里建议允许自定 ddcoder
        $filename = $param['type'] . '-' . date('YmdHis') . '(' . $param['name'] . ')';
        $this->doStart($param['title'], $filename);
    }

    public function doStart($keys, $filename)
    {
        $this->download($filename . '.xls');

        // php输出到缓冲区
        echo '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><style>td{vnd.ms-excel.numberformat:@}</style></head>';
        echo '<table width="100%" border="1">';
        // 有时候需要自定义表头 ddcdoer 修改于2017-08-26
        if ($keys) {
            echo '<tr><th filter=all>' . implode('</th><th filter=all>', $keys) . "</th></tr>\r\n";
        }

        //刷新缓冲区
        ob_flush();
        flush();
    }

    //下载文件
    //$mimeType = 'application/force-download'
    //Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
    //$mimeType = 'application/vnd.ms-excel'
    function download($fname = 'data', $data = null, $mimeType = 'application/force-download')
    {
        if (headers_sent($file, $line)) {
            echo 'Header already sent @ ' . $file . ':' . $line;
            exit();
        }
        //header('Cache-Control: no-cache;must-revalidate');
        // //fix ie download bug
        header('Pragma: no-cache, no-store');
        header("Expires: Wed, 26 Feb 1997 08:21:57 GMT");
        if (strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE')) {
            $fname = urlencode($fname);
            header('Content-type: ' . $mimeType);
        } else {
            header('Content-type: ' . $mimeType . ';charset=utf-8');
        }
        header("Content-Disposition: attachment; filename=\"" . $fname . '"');
        //header( "Content-Description: File Transfer");
        if ($data) {
            header('Content-Length: ' . strlen($data));
            echo $data;
            exit();
        }
    }

    public function csv_export($keys, $expData, $type, $count)
    {
        $csv = '';
        foreach ($keys as $key) {
            $csv .= '"' . iconv("UTF-8", "gb2312", $key) . '",';
        }
        $csv .= "\n";

        foreach ($expData as $expArr) {
            foreach ($expArr as $e_key => $exp) {
                $csv .= iconv("UTF-8", "gbk", $this->escapeCSV($expArr[$e_key])) . ',';
            }
            $csv .= "\n";
        }
        ob_end_clean();
        $fileName = $type . '-' . date('YmdHis') . '(' . $count . ').csv';
        header("Content-type:text/csv");
        header("Content-Disposition:attachment;filename=" . iconv("UTF-8", "gb2312", $fileName));
        header('Cache-Control:must-revalidate,post-check=0,pre-check=0');
        header('Expires:0');
        header('Pragma:public');
        ob_end_clean();
        echo $csv;
    }

    /**
     * 生成并下载csv文件
     */
    public function csv_export_f($keys, $expData, $fileName)
    {
        // export file
        $csv = '';
        foreach ($keys as $key) {
            $csv .= '"' . iconv("UTF-8", "gb2312", $key) . '",';
        }
        $csv .= "\n";

        foreach ($expData as $expArr) {
            foreach ($expArr as $e_key => $exp) {
                $csv .= iconv("UTF-8", "gbk", $this->escapeCSV($expArr[$e_key])) . ',';
            }
            $csv .= "\n";
        }
        ob_end_clean();
        $fileName .= '.csv';
        header("Content-type:text/csv");
        header("Content-Disposition:attachment;filename=" . iconv("UTF-8", "gb2312", $fileName));
        header('Cache-Control:must-revalidate,post-check=0,pre-check=0');
        header('Expires:0');
        header('Pragma:public');
        ob_end_clean();
        echo $csv;
    }

    function escapeCSV($str)
    {
        $str = str_replace(array(',', '"', "\n\r"), array('', '""', ''), $str);
        if ($str == "") {
            $str = '""';
        }
        return $str;
    }

    public function allData($rows)
    {
        foreach ($rows as $row) {
            echo '<tr><td>' . implode('</td><td>', $row) . "</td></tr>\r\n";
        }
        ob_flush();
        flush();
    }

    public function oneData($row)
    {
        echo '<tr><td>' . implode('</td><td>', $row) . "</td></tr>\r\n";
        ob_flush();
        flush();
    }

    function end()
    {
        echo '</table>';
        ob_flush();
        flush();
    }

    /*
     * 多条数据flush一次 默认1000,有初始化对象决定
     * */

    public function multiData($row)
    {
        $this->rowCount++;
        echo '<tr><td>' . implode('</td><td>', $row) . "</td></tr>\r\n";
        if ($this->rowCount >= $this->rowFlushCount) {
            ob_flush();
            flush();
        }

    }
}

问题分析

    1.我的xls为什么只能导出65536条

    原因: excel2003最大支持这么多条

    2.网络中断了怎么办?

    答:不用管,会自动断点续传

    3.缓冲区是个什么东西?

    答:缓冲区的作用就是,把输入或者输出的内容先放进内存

    比如:





   for($i=0; $i<10; $i++){
      echo 123;
      sleep(1)
   }


   如上,循环了10次,每次都循环执行了echo 123; 用浏览器访问上面的代码,不会立即输出123;而是等到上面这个for循环结束的时候,一次输出了所有的123,为什么不是一次输出123到浏览器,输出10次,而是等循环结束后一次性输出10个123呢?

这是因为每次循环echo 123,都放入缓冲区,等脚本执行完毕后,才把缓冲区的数据一次性输出到浏览器;


   4.清空缓存区数据不就是消失了吗?

   答:调用flush函数,数据从缓冲区发送前端用户电脑,不是消失; 注意flush函数最后和ob_flush函数一起使用;

   ob_flush; 数据从php缓冲区输出给apache等web服务

   fiush();输出从apache等web服务的缓冲区输出给前端用户;

for($i=0; $i<10; $i++){
   echo 123;
   ob_flush();
   fiush();
   sleep(1)
}

 上述代码每次输出一个123,就会输出给浏览器

MyAnswer博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论