设计导出量:百万级别,耗时为分钟。万级别,耗时为秒
分成每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文件