Laravel Excel 导出大量数据,服务器超时

1. 运行环境

2). 当前使用的版本

Laravel 版本:8.38.0
PHP 版本:7.4.3
mysql: 5.7

3). 当前系统

Windows 10

4). 业务环境

导出大量csv格式数据数据

2. 问题描述?

使用dcat/easy-excel 倒出数据是应为导出的字段需要大量查询和逻辑,导致数据量上万条时服务器请求超时,之后我使用chunkSize方法分块导出,经测试最大值为100,这样下来导致php脚本运行超时,数据整合下拉大约两万条数据,本地测试需要十几分钟,应为线上服务器打开了安全模式,所以不能设置set_time_limit(0)。之后我想到了队列,然后进行测试,在第一步直接被卡死:sob: (队列使用插件Maatwebsite/Laravel-Excel 3)

我写的导出类

<?php

namespace App\Tenant\Extensions;

use Dcat\Admin\Grid\Exporters\AbstractExporter;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;

class ExcelExpoter extends AbstractExporter implements WithMapping, WithHeadings, FromCollection, WithStrictNullComparison,ShouldAutoSize,FromQuery
{
    use Exportable;
    protected $fileName = '表格导出测试';
    protected $titles = [];

    public function __construct()
    {
        $this->fileName = $this->fileName.'_'.Str::random(6).'.csv';//拼接下载文件名称
        $this->titles = $title = [
            'coures_id'            =>'Course ID',
            'lesson_id'            =>'Lesson ID',
            'subject_id'           =>'Subject ID',
            'subject_name'         =>'Subject Name',
            'level_name'           =>'Level Name',
            'progress'             =>'Progress',
            'lesson_start_at'      =>'Lesson Start Datetime',
            'lesson_end_at'        =>'Lesson End Datetime',
            'duration'             =>'Duration',
            'max_student'          =>'Max Student',
            'of_student'           =>'of Student',
            'identity'             =>'Identity',
            'name'                 =>'Name',
            'phone'                =>'Phone Number',
            'present'              =>'Show Up',
            'preset_time'          =>'Actual Duration(m)',
            'late'                 =>'Late',
            'leave_early'          =>'Leavel Early',
            'first_entry_datetime' =>'First Entry Datetime',
            'last_exit_datetime'   =>'Last Exit Datetime'
        ];
        parent::__construct();
    }

    public function export()
    {
        // TODO: Implement export() method.
        $this->queue($this->fileName)->onQueue('exports');
        return back()->withSuccess('Export started!');
    }

    public function collection()
    {
        // TODO: Implement collection() method.

        return collect($this->buildData());
    }

    public function headings(): array
    {
        // TODO: Implement headings() method.
        return $this->titles();
    }

    public function map($row): array
    {
        // TODO: Implement map() method.
        return $row;
    }
    public function drawings()
    {
        $drawing = new Drawing();
        $drawing->setName('Logo');
        $drawing->setDescription('This is my logo');
        $drawing->setPath(public_path('/img/logo.jpg'));
        $drawing->setHeight(90);
        $drawing->setCoordinates('B3');

        return $drawing;
    }
    public function query()
    {
        return Invoice::query();
    }

}

报错信息

Serialization of 'Closure' is not allowed

Laravel Excel 导出大量数据,服务器超时

希望各位大佬给个建议哈

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

Serialization of 'Closure' is not allowed 报错是 PHP无法序列化闭包,应该是队列引用的代码里包含了闭包,根据Stack trace 报错信息找一下问题,我遇到过向Job里边传参传了整个request实例,包含了上传文件的属性,报了这个错,最后按需传参,传输组解决了。 队列导出的话,可以试试github.com/Maatwebsite/Laravel-Exc... 的队列导出功能,文件生成成功后可以用websocket传输数据通知前端下载。

2年前 评论
AlwaysProsperousYang (楼主) 2年前
Yining 1年前
讨论数量: 25

我最近也碰到这个问题,临时解决方案,让客户那边多次导出(增加筛选条件)

我感觉还是需要异步(队列)来执行导出任务,导出完毕保存到服务器上,前端通知客户,去下载。

2年前 评论
AlwaysProsperousYang (楼主) 2年前
勇敢的心 2年前

耗时任务,会阻塞整个worker进程。你不应该考虑使用异步的方式,消息队列之类的吗?

2年前 评论
AlwaysProsperousYang (楼主) 2年前
Flex

使用xlswriter C扩展来实现吧,一百万数据我试过52秒左右

2年前 评论
AlwaysProsperousYang (楼主) 2年前
porygonCN 2年前
冯小胖同学 2年前

导出csv 可参考 博客:导出csv

2年前 评论
AlwaysProsperousYang (楼主) 2年前

我导出是比较喜欢用前端,就像平常api请求分页数据那样,前端一页一页异步加载然后用js转excel,这样不仅不用担心php内存,还能直接看到导出进度

2年前 评论
AlwaysProsperousYang (楼主) 2年前
leo

一万条数据量的导出超时明显问题不会出在生成 excel、csv 文件上,大概率是 N+1 问题

2年前 评论
AlwaysProsperousYang (楼主) 2年前

Serialization of 'Closure' is not allowed 报错是 PHP无法序列化闭包,应该是队列引用的代码里包含了闭包,根据Stack trace 报错信息找一下问题,我遇到过向Job里边传参传了整个request实例,包含了上传文件的属性,报了这个错,最后按需传参,传输组解决了。 队列导出的话,可以试试github.com/Maatwebsite/Laravel-Exc... 的队列导出功能,文件生成成功后可以用websocket传输数据通知前端下载。

2年前 评论
AlwaysProsperousYang (楼主) 2年前
Yining 1年前

你这才1万多行而已,我这七八万行导出.xls呢 :dog:

2年前 评论
AlwaysProsperousYang (楼主) 2年前

异步呀, 我们的实现方式是 走队列,通知到导出服务,导出服务拿到数据接口分批循环去请求获取数据,然后组装到execl

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!