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 导出大量数据,服务器超时

希望各位大佬给个建议哈

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
最佳答案

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3年前 评论

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