大批量数据导出/前端导出数据实践

场景

我们PHP程序员在整个工作流程中,难免要和CRM/ERP之类的系统打交道。数据表格导出是在常见不过的功能模块。在数据量比较小的时候,我们可以随便搞,直接设置header头,输出csv也是可以的。但是当数据量上来的时候,我们会面临但不局限于以下问题:

1.php脚本执行超时;

2.可用内存不足;

3.nginx超时。

解决方案

所以很自然的想到,那能不能把文件下载/导出/保存这个动作放到前端来执行呢,这样还可以节省服务器资源。我觉得这是个不错的主意。
先上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//依赖的JS工具库
//github https://github.com/sheetjs/sheetjs
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
exportData() {
//dialog控制开关
this.dialogVisible = true;
/* original data */
let date = new Date();
let filename = date.getFullYear() + '' + (date.getMonth() + 1) + '' + date.getDate() + "列表.xlsx";
let insertData = [];
let header = [
'用户id',
'手机号',
'姓名'
];
insertData.push(header);
//在vue里为了不影响数据做一下深拷贝
let tmpData = JSON.stringify(this.$data);
let tmpParamObj = JSON.parse(tmpData);
let totalPage = this.total_page;
let totalCount = this.total_count;
const that = this;
for (let i = 1;i<=totalPage;i++) {
tmpParamObj.current_page = i;
//去掉没用的数据
tmpParamObj.list = [];
tmpParamObj.pagination = [];
$.ajax({
url: '/demo/demo/demo/getList',
method: 'POST',
data: {'_token': '{{csrf_token()}}', data: tmpParamObj},
success: function (data) {
console.log(data);
if (data.status == 'success') {

data.data.forEach((item, index) => {
let tmp = [];
tmp.push(item.PassportID);
tmp.push(item.MobileNumber);
tmp.push(item.RealName);
insertData.push(tmp);
});
//记录下载进度更新到dialog上的下载进度条
let percent = Number.parseInt((insertData.length - 1) / totalCount * 100);
console.log(percent);
that.download_percent = percent;
if (percent === 100) {
let ws_name = "SheetJS";
let wb = XLSX.utils.book_new();
let ws = XLSX.utils.aoa_to_sheet(insertData);
//
/* add worksheet to workbook */
XLSX.utils.book_append_sheet(wb, ws, ws_name);
console.log('【开始下载】' + new Date());
XLSX.writeFile(wb, filename);
console.log('【下载完成】' + new Date());
that.dialogVisible = false;
}
} else {
layer.msg('请求失败', {icon: 2});
}
},
dataType: 'json',
error: function () {
layer.msg('网络异常', {icon: 2}, function () {
layer.closeAll();
});
}
});
}
}

循环的按照页码去请求数据,直到拿到所有的数据。然后一并保存。理论上可以不担心数据的体量。新技能Get。

根据身份证获取所在省份对照表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$province = [
11 => '北京',
12 => '天津',
13 => '河北',
14 => '山西',
15 => '内蒙古',
21 => '辽宁',
22 => '吉林',
23 => '黑龙江',
31 => '上海',
32 => '江苏',
33 => '浙江',
34 => '安徽',
35 => '福建',
36 => '江西',
37 => '山东',
41 => '河南',
42 => '湖北',
43 => '湖南',
44 => '广东',
45 => '广西',
46 => '海南',
50 => '重庆',
51 => '四川',
52 => '贵州',
53 => '云南',
54 => '西藏',
61 => '陕西',
62 => '甘肃',
63 => '青海',
64 => '宁夏',
65 => '新疆',
71 => '台湾',
81 => '香港',
82 => '澳门'

];

如果你在laravel框架里,不妨在helper文件里写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//从身份证里取所在地区
if (!function_exists('get_province_by_card')) {
function get_province_by_card($card = '')
{
if (!empty($card)) {
$province = [
11 => '北京',
12 => '天津',
13 => '河北',
14 => '山西',
15 => '内蒙古',
21 => '辽宁',
22 => '吉林',
23 => '黑龙江',
31 => '上海',
32 => '江苏',
33 => '浙江',
34 => '安徽',
35 => '福建',
36 => '江西',
37 => '山东',
41 => '河南',
42 => '湖北',
43 => '湖南',
44 => '广东',
45 => '广西',
46 => '海南',
50 => '重庆',
51 => '四川',
52 => '贵州',
53 => '云南',
54 => '西藏',
61 => '陕西',
62 => '甘肃',
63 => '青海',
64 => '宁夏',
65 => '新疆',
71 => '台湾',
81 => '香港',
82 => '澳门'

];
return array_get($province, substr($card, 0, 2), '');
}
return $card;
}
}

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×