php 大数据excel导出 buffer配置

原理:php开启缓存区输出 --> php系统缓冲区(nginx, apache服务器) --> 浏览器缓冲区

因此,要实现边查询,边下载效果,php, web服务器, 浏览器 三个方面的配置都不能少

apache配置: httpd.conf
    由于默认开启了mod_cgi.so模块
    在http.conf配置文件最后一行加上, 表示设置缓冲区的字节大小为0,一旦有php输出,就立马响应浏览器输出
FcgidOutputBufferSize 0

注意,nginx也有设置buffer的大小
<?php
/**
 * 导出大数据抽象类
 */
abstract class LargeExportAbstract
{
    protected $filename = 'export.csv';
    protected $chunkNum = 1000;
    private $fp;
    //是否安全导出
    protected $isSafe = false;

    /**获取Query查询对象
     * @return Query
     */
    abstract public function getQuery();

    /**设置安全导出, 既相关数据不可看
     * @param bool|true $safe
     * @return $this
     */
    public function setSafeModel($safe = true)
    {
        $this->isSafe = $safe;
        return $this;
    }

    /**第一行导出
     * @return array
     */
    public function beforeRow()
    {
        return [];
    }

    /**数据导出
     * @param $row
     * @return mixed
     */
    public function formatRow($row)
    {
        return $row;
    }

    protected function outPutStart()
    {
        $this->fp = fopen('php://output', 'a');//打开output流
    }
    protected function outPutEnd()
    {
        fclose($this->fp);
    }

    /**
     * 创建IO
     */
    protected function IOStart()
    {
        header('Content-Description: File Transfer');
        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment; filename="'. $this->filename .'"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('X-Accel-Buffering: no');  //适用于Nginx服务器环境
        header('Pragma: public');
        //使用流式输出
        $this->outPutStart();
        //开启缓冲区
        @ob_clean();
        //一旦有缓冲,立马输出
        ob_implicit_flush(true);
    }

    /**
     * 关闭IO
     */
    protected function IOend()
    {
        @ob_end_clean();
        //关闭流式输出
        $this->outPutEnd();
        exit();
    }

    /**
     * 清空缓冲区, 内容进入下一层缓冲区
     */
    protected function flush()
    {
        @ob_flush();
        @flush();
    }

    /**向excel输出一行数据
     * @param $row
     */
    public function put($row)
    {
        if ($this->fp)
            fputcsv($this->fp, $row);
    }

    /**导出
     * @param string $filename 可选,文件名
     */
    public function export($filename = '')
    {
        if (!empty($filename)) {
            $this->filename = $filename;
        }

        //创建一个流式IO
        $this->IOStart();

        //数据查询前向浏览器输出
        $beforeRow = $this->beforeRow();
        if (!empty($beforeRow)) {
            $this->put($beforeRow);
        }
        //数据分批查询
        $this->getQuery()->chunk((int)$this->chunkNum, function ($rows) {
            foreach ($rows as $row) {
                $row = $this->formatRow($row);
                //存放php缓存冲区
                $this->put($row);
            }
            unset($rows);
            //php缓冲区释放, 进入下一层缓冲区
            $this->flush();
        });

        //关闭IO
        $this->IOend();
    }

    /**原样输出,防止小数导出时为空 如数字5.00,本应导出 5.00 ,实际导出了 5
     * @param $value
     * @return string
     */
    public function toText($value)
    {
        return $value."\t";
    }
}
<?php
/*
查询类服务
*/
class RankStatisticService {    
    protected $name;
    public function __construct($id, $inputs){
         ...
    }

    public function getQuery(){
        return GoodModel::where('name', $this->name);
    }

}

/**统计导出
 * Class RankStatistic
 * @package App\Services\Activity\InviteActivity
 */
class RankStatisticExport extends LargeExportAbstract{
    /**导出统计服务
     * @var \App\Services\CpaData\CpaStatisticService
     */
    protected $staticService;
    //活动数据
    protected $activity;

    protected $chunkNum = 2;
    //导出文件名
    protected $filename = '数据统计(排行榜).csv';
    //是否安全导出
    protected $isSafe = false;

    protected $headerRow = ['活动期数', '会员id', '会员所属分组', '会员手机号', '邀请人数', '排名', '是否获得奖励', '奖品名称', '收货地址'];


    public function __construct($id, $inputs)
    {
        $this->staticService = (new RankStatisticService($id, $inputs));
        $this->activity = $this->staticService->activityData();
    }

    public function getQuery()
    {
        return $this->staticService->getQuery();
    }

    public function beforeRow()
    {
        return $this->initRow($this->headerRow);
    }

    public function formatRow($v)
    {
        $row = [
            "第{$this->activity->round}期",                //期数
            $v->user_id,               //会员id
            $v->group_name,             //会员手机号
            $this->toText($v->mobile),          //会员所属分组
            $v->vip_number,               //开通权益会员数量
            $v->user_rank,                 //排名
            $v->reward_zh,          //是否获得奖励
            $v->reward_name,       //奖品名称
            $v->addressDetail       //收货地址
        ];
        
        return $this->initRow($row);
    }

    /**格式化导出的数据
     * @param $row
     * @return array
     */
    protected function initRow($row) {
        //安全模式下,不导出手机号
        if ($this->isSafe) {
            unset($row[3]);
            $row = array_values($row);
        }
        return $row;
    }
}

/*
客户端调用
*/
(new RankStatisticExport(1, [])->export();

利用上面的原理,最终的效果如下面视频: 

链接:https://pan.baidu.com/s/1vA_RnveMIUQO8FAQt774ig 
提取码:7pdj 
复制这段内容后打开百度网盘手机App,操作更方便哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值