原理: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,操作更方便哦
php 大数据excel导出 buffer配置
最新推荐文章于 2023-10-26 09:00:00 发布