0

0

php如何大批量导出excel数据

醉折花枝作酒筹

醉折花枝作酒筹

发布时间:2021-06-28 16:29:30

|

3889人浏览过

|

来源于segmentfault

转载

在平时生活或其他时候,我们可能会需要大批量导出excel数据,所以将这次的优化过程发布出来,希望对有需要的同学启到一定的帮助。

php如何大批量导出excel数据

项目后台有导出几 w 条数据生成 excel 的功能,刚好前同事的方法直接报内存溢出错误,
所以将这次的优化过程发布出来,希望对有需要的同学启到一定的帮助

先看优化后效果:

bVcK7oI.webp.jpg

立即学习PHP免费学习笔记(深入)”;

异步生成数据且真实进度条反馈进度

bVcK7oL.webp.jpg

2.进度完成,前端 js 跳转到下载地址

bVcK7oR.webp.jpg

3.生成 csv 文件代替 excel , 3.5w 条数据文件大小 8M

原代码报错信息

[2020-09-27 09:21:13] local.ERROR: Allowed memory size of 536870912 bytes exhausted (tried to allocate 8192 bytes) {"userId":1,"exception":"[object] (Symfony\Component\Debug\Exception\FatalErrorException(code: 1): Allowed memory size of 536870912 bytes exhausted (tried to allocate 8192 bytes) at /Users/****/WebRoot/ValeSite/****/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:879)

原代码逻辑

$list = Good::with(['good_standard', 'good_standard.default_picture',  'good_standard.brand'])
......
->selectRaw('goods.*')~~~~
->get();
#内存溢出点 1, 该 orm 返回数据量为 3.5w 行数据
...... ~~~~
$list = $this->goodsRepository->batchGetFullGoodsScope($list);
foreach ($list as $item) {
   $cell = [];
   .....
   //没条数组共 30 个元素   
   $cellData[] = $cell;
}
# 内存溢出点 2 ,生成需要的数据,3w + 条数据时,内存消耗大概在 110M +

.....
Excel::create(...)
#内存溢出点 3 , Maatwebsite/Laravel-Excel库大批量生成也会内存溢出

# 和直观的代码处理流,该代码在小数据量时无问题

解决思路分析

  1. orm 取数据优化 (mysql)

  2. 对已获取的 orm 数据二次处理后 , 数据存储优化

  3. 导出 excel 时, 导出优化

后续所有的代码功能都是围绕该 3 个方向来处理


方案 1 (异步生成数据 )

思路分析:

前端 ajax 发送 excel 导出请求  ->后端结束请求且计算数据总条数, 按一定倍数拆分成多次 job 生成数据 ->后端多个进程异步执行 job 队列任务,按批次生成数据 (每次执行计数一次,数据写入 redis) ->前端 ajax 轮询获取总次数和当前已执行次数 (计算出进度条 ) ->前端获 ajax 轮询结果总次数 = 已执行次数 ~~~~(进度100%),跳转到下载地址 ->后端 redis 取数据,渲染生成 csv 文件(下载完成)

代码实现:

前端代码: jquery + Bootstrap

# 进度条样式,可在 bootstrap 找到
<section class="panel" id="export_loading_box">
    <p class="panel-body m-b-10" style="display: none">
        <p class="text-muted">
            数据导出中 .....
        </p>
        <p class="progress progress-striped active">
            <p class="progress-bar progress-bar-info"
 role="progressbar"
 aria-valuemin="0"
 aria-valuemax="100"
 style="width: 0%">
                0%
            </p>
        </p>
    </p>
</section>
$(function () {
    $('.down-list').click(function () {
        let formName = $(this).attr('data-form')
        let data = $('#' + formName).serialize();
        OE.params.url = $(this).attr('data-url');
        OE.handle(data);
    });
})
//商品导出 JS 控件
let OE = window.OE || {}
OE = {
    params: {
        ifRun: 0,
        url: '',
        cachePre: '',
    },
    # 1. 前端 ajax 发送 excel导出请求
    handle: function (formData) {
        if (OE.params.ifRun) {
            return false;
        }
        OE.params.ifRun = 1;
        OE.rateShow(100, 0);
        $('#export_loading_box').find('.panel-body').show();
        $.getJSON(OE.params.url, formData + '&run=1', function (data) {
            if (200 == data.status) {
                OE.params.cachePre =  data.cachePre;
                //请求成功, 渲染进度条
                OE.init();
            } else {
                OE.showAlert(false, data.msg);
            }
        })
    },
    # 4. ajax 轮询渲染进度条
    init: function () {
        let t = setInterval(function () {
            $.getJSON(OE.params.url, "get_run=1&cache_pre="+OE.params.cachePre, function (data) {
                OE.rateShow(data.total, data.run);
                if (200 == data.status && data.total == data.run) {
                    clearInterval(t);
                    OE.params.ifRun = 0;
                    OE.showAlert(true);
                    //跳转下载 excel
 window.location.href = OE.params.url+'?cache_pre='+OE.params.cachePre;
                    return;
                }
            });
        }, 2000);
    },
    showAlert: function (success, msg) {
        if (success) {
            html = '<p class="alert alert-success fade in">' +
                '       <button data-dismiss="alert" class="close close-sm" type="button">' +
                '           <i class="fa fa-times"></i>' +
                '       </button>' +
                '       <strong>导出成功</strong> 数据下载中' +
                ' </p>';
        }
        $('#export_loading_box').append(html);
        $('#export_loading_box').find('.panel-body').hide();
    },
    # 进度条计算
    rateShow: function (total, run) {
        let width = ((run / total) * 100).toFixed(0);
        $('#export_loading_box').find('.progress-bar').css('width', width + '%');
        $('#export_loading_box').find('.progress-bar').text(width + '% ');
    }
}

后端代码 :

2.后端总入口

// 前端第一次请求触发代码实现
$listOrm = self::getGoodOrm();

//求总,初始化 job 数据
$total = $listOrm->count();
for ($page = 0; $page <= ($totalPage - 1); $page++) {
            //创建子队列
            CreateExportGoodData::dispatch($requestData, $page, $authAdmin->id, $cachePre)
                ->onQueue(self::JOB_QUEUE);
 }

3.后端异步子队列执行任务 (和前端无关)

self::$requestData = $requestData;
$listOrm = self::getGoodOrm();
$list = $listOrm->offset($page * self::PAGE_NUM)
 ->limit(self::PAGE_NUM)
 ->orderByDesc('goods.id')
 ->get();
 
 //数据清洗过程
 ......
 
 //执行次数递增 1
Redis::incr(self::GE_RUN_KEY . $cachePre . ':' . $adminId);
//清洗后的数据压入 redis 列表
Redis::lpush(self::GE_DATA_KEY . $cachePre . ':' . $adminId, serialize($data));

4.后端实现前端 ajax 轮询执行进度反馈代码实现

Shop7z网上购物系统至尊版
Shop7z网上购物系统至尊版

Shop7z网上购物系统支持电脑版+手机版+支付宝及微信支付,支持QQ和微信一键登陆,系统集众家之所长,大气超美观页面+手机版+商品组合套餐+限时抢购秒杀+图片批量上传+淘宝数据包导入+弹出式分类菜单+不同规格不同价格+新订单邮件通知+销售报表打印与Excel输出+物流跟踪打印查询+会员积分及优惠券+邮件群发+图片在线管理+销售统计报表+五种价格体系+礼品礼券+微信公众号支付+扫码支付等等等。

下载
$total = Redis::get(GoodsExportRepository::GE_TOTAL_KEY. $cachePre. ':'. $authAdmin->id);
$run = Redis::get(GoodsExportRepository::GE_RUN_KEY. $cachePre. ':'. $authAdmin->id);
if ($request->input('get_run')) {
 //前端 ajax 轮询获取同步已运行队列数
 return ['status' => 200, 'total' => $total, 'run' => $run];
}

6.后端实现前端 excel 下载代码实现

$fileName = "商品导出" . date('Y-m-d');
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="' . $fileName . '.csv"');
header('Cache-Control: max-age=0');
//开启预输出流
$fp = fopen('php://output', 'a');

//输出商品列表数据
while (true) {
    //核心1 从 redis 列表里依次取数据
    $data = Redis::rpop(self::GE_DATA_KEY . $cachePre . ':' . $adminId);
    if (!$data) {
        // redis 列表数据为空,结束 while 循环
        break;
    } 
    //核心2 
    ob_flush(); //取出 $fb 输出流 存入 buffer 内数据
    flush();    //直接渲染至 http 数据流至浏览器
    $data = unserialize($data);
    foreach ($data as $row) {
        foreach ($row as $key => $value) {
            if (is_string($value)) {
               $row[$key] = iconv('utf-8', 'gbk//IGNORE', $value);
            } 
        } 
        fputcsv($fp, $row);
    }
 }
fclose($fp);
//必须 exit 阻止框架继续输出
exit();

至此,异步导出 excel 已完成

总结:

  • 后端队列任务生产数据录入 redis list

  • 前端 ajax 轮询获取执行情况

  • 前端 获取后端已将所有 队列任务执行, 跳转至下载地址

  • 下载地址取 redis 内数据,渲染成 csv 文件

优点:

  • 异步多队列进程执行,效率高

  • 前端可实时获取执行进度, 用户体验好

缺点:

  • ajax 轮询占用正常用户请求资源,该方案只适合后台实现

  • 代码复杂, 施工人员需一定的 laravel 队列知识和 前端知识储备;对自己把握不足的同学可直接看第二种解决方案


方案 2 (同步生成数据 )

思路分析:
  • 设置 php 脚本时间 set_time_limit(0);

  • orm 依次获取数据,对获取的数据直接清洗后直接写入 输出流, 输出至浏览器

代码实现:

set_time_limit(0)

//直接输出头部声明
$fileName = "商品导出" . date('Y-m-d');
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="' . $fileName . '.csv"');
header('Cache-Control: max-age=0');

//开启输出流
$fp = fopen('php://output', 'a');

// while 循环取数据
$page = 0;
while (true) {
    $listOrm = self::getGoodOrm();
    $list = $listOrm->offset($page * self::PAGE_NUM)
            ->limit(self::PAGE_NUM)
            ->orderByDesc('goods.id')
            ->get();
  if ($list->isEmpty()) {
    //无数据时退出 while 循环
    break;
  }
  //数据清洗
  $data = ..... 
  
  //直接将清洗后的 $data 数据写入输出流
  foreach ($data as $row) {
        foreach ($row as $key => $value) {
            if (is_string($value)) {
                  $row[$key] = iconv('utf-8', 'gbk//IGNORE', $value);
            }
        }
        fputcsv($fp, $row);
   }
  
  //输出至浏览器
  ob_flush();
  flush(); 
}
 fclose($fp);
 exit();

总结

  • 优点: 代码流程简单,开发难度低

  • 缺点: 前端体验差, ( 数据单进程获取,效率低) 下载等待耗时长 )


不管是异步还是同步, 实际思路都是分页获取数据, 对分页获取的数据进行处理; 都有用到核心方法:

fopen('php://output', 'a') , 
ob_flush(), 
flush();

推荐学习:php视频教程

相关文章

WPS零基础入门到精通全套教程!
WPS零基础入门到精通全套教程!

全网最新最细最实用WPS零基础入门到精通全套教程!带你真正掌握WPS办公! 内含Excel基础操作、函数设计、数据透视表等

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
laravel组件介绍
laravel组件介绍

laravel 提供了丰富的组件,包括身份验证、模板引擎、缓存、命令行工具、数据库交互、对象关系映射器、事件处理、文件操作、电子邮件发送、队列管理和数据验证。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

340

2024.04.09

laravel中间件介绍
laravel中间件介绍

laravel 中间件分为五种类型:全局、路由、组、终止和自定。想了解更多laravel中间件的相关内容,可以阅读本专题下面的文章。

293

2024.04.09

laravel使用的设计模式有哪些
laravel使用的设计模式有哪些

laravel使用的设计模式有:1、单例模式;2、工厂方法模式;3、建造者模式;4、适配器模式;5、装饰器模式;6、策略模式;7、观察者模式。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

772

2024.04.09

thinkphp和laravel哪个简单
thinkphp和laravel哪个简单

对于初学者来说,laravel 的入门门槛较低,更易上手,原因包括:1. 更简单的安装和配置;2. 丰富的文档和社区支持;3. 简洁易懂的语法和 api;4. 平缓的学习曲线。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

385

2024.04.10

laravel入门教程
laravel入门教程

本专题整合了laravel入门教程,想了解更多详细内容,请阅读专题下面的文章。

141

2025.08.05

laravel实战教程
laravel实战教程

本专题整合了laravel实战教程,阅读专题下面的文章了解更多详细内容。

85

2025.08.05

laravel面试题
laravel面试题

本专题整合了laravel面试题相关内容,阅读专题下面的文章了解更多详细内容。

80

2025.08.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

463

2026.03.04

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号