首先说明几点:
1,excel格式的文件最大支持100万的数据,所以不考虑使用excel格式
2,laravel的toArray()方法有内存泄露,所以大量数据导出不能使用.
3,当然要使用chunk方法查询数据,然后写到文件中
关于toArray()方法内存泄露排查
chunk()方法的代码块如下(chunk中按理是不用unset的,会自动释放):
q
u
e
r
y
−
>
c
h
u
n
k
(
1000
,
f
u
n
c
t
i
o
n
(
query->chunk(1000, function (
query−>chunk(1000,function(data) use (&$firstWrite, $fp) {
Log::info(“开始:”.memory_get_usage());
$data =
d
a
t
a
−
>
t
o
A
r
r
a
y
(
)
;
L
o
g
:
:
i
n
f
o
(
"
结
束
:
"
.
m
e
m
o
r
y
g
e
t
u
s
a
g
e
(
)
)
;
u
n
s
e
t
(
data->toArray(); Log::info("结束:".memory_get_usage()); unset(
data−>toArray();Log::info("结束:".memorygetusage());unset(data);
Log::info(“usnet 结束:”.memory_get_usage());
});
在将一个“组块”的 Eloquent 模型转为数组的时候$data = $data->toArray();内存增加了很多.最后unset的时候又没有释放回初始值.实际上chunk中是不需要unset释放的.
解决
使用
DB::table(“xxx”)->->orderBy(xx)->chunk(xx);方式查询数据,查询结果在使用
d
a
t
a
=
j
s
o
n
d
e
c
o
d
e
(
j
s
o
n
e
n
c
o
d
e
(
data = json_decode(json_encode(
data=jsondecode(jsonencode(data), true);转换成数组,每次chunk执行进去,内存都是一样的,内部也不需要unset.
大数据导出最后做的方案
因为数据量大,所以我的做法是,小量数据导出就直接发送给用户.大数据导出,走队列任务,然后做一个报表管理,用户可以查看进度和完成后下载.
在浏览器中直接返回文件和后台写入文件
函数基本使用
f
p
=
f
o
p
e
n
(
"
文
件
路
径
/
文
件
名
"
,
"
a
"
)
;
/
/
a
表
示
追
加
模
式
f
w
r
i
t
e
(
fp = fopen("文件路径/文件名", "a"); //a表示追加模式 fwrite(
fp=fopen("文件路径/文件名","a");//a表示追加模式fwrite(fp, chr(0xEF).chr(0xBB).chr(0xBF)); // 添加 BOM,不然excel打开csv是乱码
在chunk的闭包中写数据: fputcsv($fp,
a
r
r
a
y
)
;
f
c
l
o
s
e
(
array); fclose(
array);fclose(fp);
在队列中导出
f
p
=
f
o
p
e
n
(
s
t
o
r
a
g
e
p
a
t
h
(
′
a
p
p
/
p
u
b
l
i
c
/
e
x
p
o
r
t
s
′
)
.
"
/
"
.
fp = fopen(storage_path('app/public/exports')."/".
fp=fopen(storagepath(′app/public/exports′)."/".report->name, “a”);
fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF)); // 添加 BOM
$firstWrite = true;
$query =
e
x
p
o
t
e
r
−
>
c
u
s
t
o
m
Q
u
e
r
y
(
expoter->customQuery(
expoter−>customQuery(query);
q
u
e
r
y
−
>
o
r
d
e
r
B
y
(
query->orderBy(
query−>orderBy(tableName.".id")->chunk(1000, function (KaTeX parse error: Expected 'EOF', got '&' at position 12: data) use (&̲firstWrite, $fp, $expoter) {
d
a
t
a
=
j
s
o
n
d
e
c
o
d
e
(
j
s
o
n
e
n
c
o
d
e
(
data = json_decode(json_encode(
data=jsondecode(jsonencode(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);
在浏览器中返回数据
r
e
s
p
o
n
s
e
=
n
e
w
S
t
r
e
a
m
e
d
R
e
s
p
o
n
s
e
(
n
u
l
l
,
200
,
[
′
C
o
n
t
e
n
t
−
T
y
p
e
′
=
>
′
t
e
x
t
/
c
s
v
′
,
′
C
o
n
t
e
n
t
−
D
i
s
p
o
s
i
t
i
o
n
′
=
>
′
a
t
t
a
c
h
m
e
n
t
;
f
i
l
e
n
a
m
e
=
"
′
.
response = new StreamedResponse(null, 200, [ 'Content-Type' => 'text/csv', 'Content-Disposition' => 'attachment; filename="'.
response=newStreamedResponse(null,200,[′Content−Type′=>′text/csv′,′Content−Disposition′=>′attachment;filename="′.fileName.’"’,
]);
r
e
s
p
o
n
s
e
−
>
s
e
t
C
a
l
l
b
a
c
k
(
f
u
n
c
t
i
o
n
(
)
u
s
e
(
response->setCallback(function () use (
response−>setCallback(function()use(query, $tableName) {
o
u
t
=
f
o
p
e
n
(
′
p
h
p
:
/
/
o
u
t
p
u
t
′
,
′
w
′
)
;
f
w
r
i
t
e
(
out = fopen('php://output', 'w'); fwrite(
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框架下处理大数据导出时遇到的问题及解决方案,包括避免使用toArray()方法防止内存泄露,采用chunk()方法分批处理数据,以及通过队列任务实现大数据导出的异步处理。
&spm=1001.2101.3001.5002&articleId=83114600&d=1&t=3&u=403fdcc348b344feabcd6cb4fcd84571)
1214

被折叠的 条评论
为什么被折叠?



