我的编程空间,编程开发者的网络收藏夹
学习永远不晚

php怎么大批量导出excel数据

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

php怎么大批量导出excel数据

本篇内容介绍了“php怎么大批量导出excel数据”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

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

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

先看优化后效果:

php怎么大批量导出excel数据

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

php怎么大批量导出excel数据

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

php怎么大批量导出excel数据

生成 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 /UsersWebRoot/ValeSitevendor/laravel/framework/class="lazy" data-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 + '% ');    }}

后端代码 :

后端总入口

// 前端第一次请求触发代码实现$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); }

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

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

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

$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];}

后端实现前端 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怎么大批量导出excel数据”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

php怎么大批量导出excel数据

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

php怎么大批量导出excel数据

本篇内容介绍了“php怎么大批量导出excel数据”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在平时生活或其他时候,我们可能会需要大批量导
2023-06-20

导出大数据方法。批量导BOM

create table CUX_IMPORT_DATA_E45 asselect * from CUX_IMPORT_DATA_L11where 1>2; CREATE TABLE CUX.lmh_segment1_E41 ASSELECT * FROM C
导出大数据方法。批量导BOM
2020-06-18

oracle怎么批量导入大量数据

Oracle数据库可以通过多种方式批量导入大量数据,以下是一些常用的方法:SQLLoader工具:SQLLoader是Oracle提供的一个用于高效加载大量数据的工具,可以通过控制文件定义数据的格式和加载规则,然后使用SQL*Loader命
oracle怎么批量导入大量数据
2024-04-16

SQLSERVER怎么大批量导入数据

在SQL Server中,可以使用以下方法进行大批量数据导入:1. 使用BULK INSERT语句:BULK INSERT语句允许从外部数据文件中导入数据到SQL Server表中。首先,确保你的数据文件符合SQL Server的要求(例如
2023-08-18

SQL Server怎么批量导入和导出数据

在SQL Server中,可以使用bcp命令行实用程序批量导入和导出数据。以下是一些示例:1. 批量导入数据:使用bcp命令行实用程序将数据从文本文件导入到表中。例如,以下命令将从名为data.txt的文本文件中导入数据到名为dbo.MyT
2023-10-10

mongodb怎么导出大量数据

要导出大量数据,可以使用MongoDB的mongodump工具来备份整个数据库或指定集合。以下是使用mongodump导出大量数据的步骤:打开命令行窗口,并进入MongoDB的bin目录(通常在MongoDB的安装目录下的bin目录中)。运
mongodb怎么导出大量数据
2024-05-22

服务器怎么批量导入与导出数据

这篇文章主要讲解了“服务器怎么批量导入与导出数据”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“服务器怎么批量导入与导出数据”吧!在使用IIS7远程桌面时,若有多个服务器需要添加,而逐一进行选
2023-06-05

MySQL批量导入Excel数据【超详细】

查看此文章前强烈建议先看这篇文章:Java江湖路 | 专栏目录 今天遇到一个需求,批量更新数据库中全国各地的物流价格,产品经理很贴心的为我做好了数据表格,说是上一个技术也是这么做的,好,压力给到我这边。话不多说,直接上步骤。 1
2023-08-18

plsql如何批量导出数据

在PL/SQL中,可以使用游标和循环语句来批量导出数据。以下是一种可能的方法:1. 创建一个游标,用于查询需要导出的数据。2. 使用游标的`OPEN`语句打开游标。3. 使用`FETCH`语句从游标中获取一条数据。4. 使用`UTL_FIL
2023-09-08

oracle怎么批量导出表结构和数据

在Oracle数据库中,可以使用expdp命令来进行表结构和数据的批量导出操作。以下是一个示例:打开命令行窗口,输入以下命令连接到数据库:sqlplus username/password@database使用expdp命令来进行表结构和数
oracle怎么批量导出表结构和数据
2024-04-19

plsql怎么快速导出大量数据

要快速导出大量数据,可以使用PL/SQL Developer提供的数据导出功能。以下是一种快速导出大量数据的步骤:1. 打开PL/SQL Developer,并连接到目标数据库。2. 在“Object Browser”窗口中,找到要导出数据
2023-09-28

PHP中怎么批量导出csv文件

PHP中怎么批量导出csv文件,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。PHP批量导出csv文件的主要程序部分:< ?php function getmicrotime(
2023-06-17

mongodb怎么导出数据为excel

要将 MongoDB 数据导出为 Excel,你可以按照以下步骤进行操作:1. 在 MongoDB 中编写一个查询来获取要导出的数据。例如,使用 `db.collection.find()` 方法来获取集合中的所有文档,或者使用其它的查询条
2023-08-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录