导出百万级数据

设计导出量:百万级别,耗时为分钟。万级别,耗时为秒
分成每3万为一个.csv文件,然后再压缩成.zip文件,统一导出.zip文件
封装导出class,简化代码,方便后期的重复调用与维护
欢迎大家改正优化

CommonExport.php文件

<?php

namespace App\Exports;

use App\Http\Controllers\Controller;

/**
 * 导出
 * @author cjg
 * @email  1049060922@qq.com
 */
class CommonExport extends Controller
{
    public function index($titles, $sql, $limit = 30000, $excelName)
    {
        set_time_limit(0);	// 执行时间(0 无上限)
        ini_set('memory_limit', '1024M');	// 内存限制(-1 无上限)
        ini_set('max_execution_time', 0);	// 程序执行时间(0无上限)
        $excelPaths = [];
        for ($i = 0; $i < 9999; $i++) {
            $offset = $i * $limit;
            $data = $this->listData($sql, $offset, $limit);
            if ($data->isEmpty()) {
                break;
            }
            $number = $i + 1;
            $excelPaths[] = $this->collectionToZipPath($data, $titles, $excelName, $number);
        }
        $zipFileName = $this->excelToZip($excelPaths, $excelName);
        return $zipFileName;
//        return $this->excelDown($zipFileName, $excelName);
    }

    public function listData($sql, $offset, $limit)
    {
        set_time_limit(0);
        ini_set('memory_limit', '1024M');
        ini_set('max_execution_time', 0);
//        DB::enableQueryLog();
        $data = $sql->offset($offset)->limit($limit)->get();
        return $data;
    }

    private function collectionToZipPath($collection, array $titles, $excelName, $number)
    {
        set_time_limit(0);
        ini_set('memory_limit', '1024M');
        $path = storage_path('app/excelOriginal/' . $excelName = time() . mt_rand(1111, 9999) . '-' . $number . '.csv');
        $fp = fopen($path, 'a');    //打开php文件句柄,php://output表示直接输出到PHP缓存,a表示将输出的内容追加到文件末尾
        $head = $titles;  //表头信息
        foreach ($head as $k => $v) {
            $head[$k] = iconv("UTF-8", "GBK//IGNORE", $v);    //将utf-8编码转为gbk。理由是: Excel 以 ANSI 格式打开,不会做编码识别。如果直接用 Excel 打开 UTF-8 编码的 CSV 文件会导致汉字部分出现乱码。
        }
        fputcsv($fp, $head);  //fputcsv() 函数将行格式$head化为 CSV 并写入一个打开的文件$fp。
        if (!empty($collection)) {
            $data = [];  //要导出的数据的顺序与表头一致;提前将最后的值准备好(比如:时间戳转为日期等)
            foreach ($collection as $key => &$val) {
            //这里是要处理的字段,要根据自己的情况改哦!
                if (!empty($val->order_no)) {
                    $val->order_no = "\t".$val->order_no;	//数字超过15位处理
                }
                if (!empty($val->settle_date)) {
                    $val->settle_date = "\t".$val->settle_date;	//年月处理
                }
            //这里是要处理的字段 end
//                foreach ($val->toArray() as $i => $item) {  //$item为一维数组哦
                foreach ($val as $i => $item) {//改为toArray()速度会慢点,自己根据情况修改
                    $data[$i] = iconv("UTF-8", "GBK//IGNORE", $item);  //转为gbk的时候可能会遇到特殊字符‘-’之类的会报错,加 ignore表示这个特殊字符直接忽略不做转换。
                }
                fputcsv($fp, $data);
            }
            return $path;  //记得加这个,不然会跳转到某个页面。
        }
    }

    public function excelToZip($excelPaths, $excelName)
    {
        $zipFileName = storage_path('app/excelZip/' . $excelName . time() . mt_rand(1111, 9999) . '.zip');   //压缩包(ZIP)名称
        $zip = new \ZipArchive;
        if ($zip->open($zipFileName, \ZipArchive::CREATE) !== TRUE) {
            throw new \Exception('创建压缩文件失败');
        }
        foreach ($excelPaths as $f) {
            if (!empty($f)) {
                $zip->addFromString(pathinfo($f)['basename'], file_get_contents($f));
            }
            unlink($f);
        }
        $zip->close();
        return $zipFileName;
    }

    private function excelDown($zipFileName, $excelName)
    {
        $aliasFileName = $excelName . date("Ymd", time()) . '.zip';
        return response()->download($zipFileName, $aliasFileName)->deleteFileAfterSend(true);
    }
}

调用的文件

public function exports(Request $request)
    {
        $data = $request->all();

        switch ($data['name']) {
            case "users":
                $res = $this->users();//用户列表
                break;
            //自己添加
        }
        $exports = (new \App\Exports\CommonExport)->index($res['titles'], $res['sql'], $res['limit'], $res['excelName']);
        $aliasFileName = $res['excelName'] . date("YmdHis", time()) . '.zip';

        return response()->download($exports, $aliasFileName)->deleteFileAfterSend(true);
    }
public function users() {
        $titles = ['呢称', '用户id', '手机号', '性别'];
        $sql = DB::table('users as u')
            ->select('a.name', 'u.user_no', 'u.phone',
            ->leftJoin('admins as a', 'u.id', '=', 'a.user_id')
                DB::raw("CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '未知' END as sex"),
            )
            ->orderBy('u.created_at', 'desc');

        return [
            'titles' => $titles,
            'sql' => $sql,
            'limit' => 30000,
            'excelName' => '用户列表导出'
        ];
    }

上面的是我自己的项目,原理懂了,根据自己的情况修改,导出万级别的时间是秒数的,百万的分钟的
上面每3万条数据一个csv文件,再压缩成zip文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值