美文网首页
laravel实现大数据csv导出

laravel实现大数据csv导出

作者: never615 | 来源:发表于2017-04-14 23:40 被阅读2981次

    首先说明几点:

    1. excel格式的文件最大支持100万的数据,所以不考虑使用excel格式
    2. laravel的toArray()方法有内存泄露,所以大量数据导出不能使用.
    3. 当然要使用chunk方法查询数据,然后写到文件中

    关于toArray()方法内存泄露排查

    chunk()方法的代码块如下(chunk中按理是不用unset的,会自动释放):

     $query->chunk(1000, function ($data) use (&$firstWrite, $fp) {
        Log::info("开始:".memory_get_usage());
        $data = $data->toArray();
        Log::info("结束:".memory_get_usage());
        unset($data);
        Log::info("usnet 结束:".memory_get_usage());
    });
    

    日志

    从下往上看

    Screen Shot 2017-04-01 at 4.16.38 PM.png

    通过日志我们可以分析得出:
    在将一个“组块”的 Eloquent 模型转为数组的时候$data = $data->toArray();内存增加了很多.最后unset的时候又没有释放回初始值.实际上chunk中是不需要unset释放的.

    解决

    使用
    DB::table("xxx")->->orderBy(xx)->chunk(xx);方式查询数据,查询结果在使用$data = json_decode(json_encode($data), true);转换成数组,每次chunk执行进去,内存都是一样的,内部也不需要unset.

    大数据导出最后做的方案

    因为数据量大,所以我的做法是,小量数据导出就直接发送给用户.大数据导出,走队列任务,然后做一个报表管理,用户可以查看进度和完成后下载.

    在浏览器中直接返回文件和后台写入文件

    函数基本使用

    $fp = fopen("文件路径/文件名", "a");  //a表示追加模式
    fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF)); // 添加 BOM,不然excel打开csv是乱码
    在chunk的闭包中写数据: fputcsv($fp, $array);
    fclose($fp);
    

    在队列中导出

     $fp = fopen(storage_path('app/public/exports')."/".$report->name, "a");
                fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF)); // 添加 BOM
                $firstWrite = true;
                $query = $expoter->customQuery($query);
                $query->orderBy($tableName.".id")->chunk(1000, function ($data) use (&$firstWrite, $fp, $expoter) {
                    $data = json_decode(json_encode($data), true);
    
                    $data = $expoter->customData($data);
                    //有一些列总是不导出,如icon,image,images
                    $data = ExportUtils::removeInvalids($data);
                    //写列名
                    if ($firstWrite) {
                        $columnNames = [];
                        //获取列名
                        foreach ($data[0] as $key => $value) {
                            $columnNames[] = admin_translate($key, "coupon");
                        }
                        fputcsv($fp, $columnNames);
                        $firstWrite = false;
                    }
                    foreach ($data as $item) {
                        fputcsv($fp, $item);
                    }
                });
    
                fclose($fp);
    

    在浏览器中返回数据

     $response = new StreamedResponse(null, 200, [
                    'Content-Type'        => 'text/csv',
                    'Content-Disposition' => 'attachment; filename="'.$fileName.'"',
                ]);
                $response->setCallback(function () use ($query, $tableName) {
                    $out = fopen('php://output', 'w');
                    fwrite($out, chr(0xEF).chr(0xBB).chr(0xBF)); // 添加 BOM
    
                    $firstWrite = true;
    
                    $query = $this->customQuery($query);
    
                    $query->orderBy($tableName.".id")->chunk(500, function ($data) use (&$firstWrite, $out) {
    
                        $data = json_decode(json_encode($data), true);
    
                        $data = $this->customData($data);
                        //有一些列总是不导出,如icon,image,images
                        $data = ExportUtils::removeInvalids($data);
                        //写列名
                        if ($firstWrite) {
                            $columnNames = [];
                            //获取列名
                            foreach ($data[0] as $key => $value) {
                                $columnNames[] = admin_translate($key, "coupon");
                            }
                            fputcsv($out, $columnNames);
                            $firstWrite = false;
                        }
                        foreach ($data as $item) {
                            fputcsv($out, $item);
                        }
                    });
    
                    fclose($out);
                });
                $response->send();
    

    相关文章

      网友评论

          本文标题:laravel实现大数据csv导出

          本文链接:https://www.haomeiwen.com/subject/jluiottx.html