Excel 自定义列导出

本文最后更新于:2024年4月26日 晚上

Excel 自定义列导出

最近项目里接到一个需求,有一个数据列表页,展示的数据列较多,然后有一个导出的功能,可以根据条件筛选导出 Excel ;但是现在用户只想按需导出指定列的数据,以及没勾选指定行时,导出全部数据,勾选了一行或多行时,导出指定行的数据。
对于第二个要求来说,其实并不麻烦,无非就是根据是否勾选在 sql 中进行指定行过滤,主要是第一个需求比较麻烦。

使用 EasyExcel 实现导出功能

经过调研,发现 EasyExcel 在导出大量数据方面性能比较好,内存也不会占据很多,所以使用 EasyExcel 实现导出功能,而不是原生 apache poi 实现。
引入 EasyExcel 依赖:

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>

定义导出的 Excel 类 (使用了 lombok ,需要引入):

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
@Data
public class User {

@ExcelProperty("用户ID")
private Integer id;

@ExcelProperty("用户名")
private String username;

@ExcelProperty("手机号")
private String phone;

@ExcelProperty("邮箱")
private String email;

@ExcelProperty("年龄")
private Integer age;

@ExcelProperty("地址")
private String addresses;

@ExcelProperty("创建时间")
private Date createTime;

@ExcelProperty("更新时间")
private Date updateTime;
}

写入到 Excel 中并且返回文件流给前端,下面是代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@PostMapping("/export")
public void exportUser(@RequestBody User user, HttpServletResponse response)
throws IOException {
List<User> userList = userService.selectList(user);

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String fileName = URLEncoder.encode("用户列表", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
response.setCharacterEncoding("UTF-8");

EasyExcel.write(response.getOutputStream(), User.class)
.excelType(ExcelTypeEnum.XLSX)
.sheet("用户导出").doWrite(userList);
}

实现可勾选行和自定义列导出

首先我们要改造传递的 RequestBody 传递的参数,需要支持条件筛选,指定列导出,勾选指定行导出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Data
public class ExportRequest implements Serializable {

/**
* 要导出的字段名字
*/
private List<String> selectFields;

/**
* 过滤的条件
*/
private User user;

/**
* 选中的ID
*/
private List<Integer> selectIds;

}

业务层详细代码:当没有传递指定列导出时,则使用默认字段集合导出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void exportBySelect(ExportRequest exportRequest, HttpServletResponse response) throws IOException {
List<String> defaultExportFields = new ArrayList<>(Arrays.asList("id", "username", "email", "age", "addresses", "phone",
"createTime", "updateTime"));

List<User> userList = userMapper.selectList(exportRequest.getselectIds(), exportRequest.getUser(),
CollectionUtils.isEmpty(exportRequest.getSelectFields()) ? defaultExportFields : exportRequest.getSelectFields());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String fileName = URLEncoder.encode("用户列表", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
response.setCharacterEncoding("UTF-8");

EasyExcel.write(response.getOutputStream(), User.class)
.excelType(ExcelTypeEnum.XLSX)
.sheet("用户导出").doWrite(userList);

}

mapper 层详细代码:使用 if 标签对每个字段进行判断是否进行参与查询结果。

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
<select id="selectByDynamicField" resultType="com.ruoyi.garden.domain.excel.FeedBackExcel">
select
<if test="'id' in selectFields">
id,
</if>
<if test="'username' in selectFields">
username,
</if>
<if test="'phone' in selectFields">
phone,
</if>
<if test="'email' in selectFields">
email
</if>
<if test="'age' in selectFields">
age,
</if>
<if test="'createTime' in selectFields">
create_time,
</if>
<if test="'updateTime' in selectFields">
update_time
</if>
from user
<where>
<if test="username != null and username != ''">
AND username like concat('%', #{username}, '%')
</if>
<if test="phone != null and phone != ''">
AND phone like concat('%', #{phone}, '%')
</if>
<if test="email != null and email != ''">
AND email like concat('%', #{email}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
<if test="createTime != null">
AND create_time = #{createTime}
</if>
<if test="updateTime != null">
AND update_time = #{updateTime}
</if>
<if test="selectIds != null and selectIds.size != 0">
and id in
<foreach collection="selectIds" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</where>
ORDER BY create_time DESC
</select>

总结

以上的代码实现了简单的自定义列导出到 Excel 中,但是如果其中其实是还有很多问题的,例如:

  • 如果要实现多表连接,那么要维护的默认字段也会越来越多,同时 xml 方法也会越来越长。
  • 如果多个表之间存在相同的名字,那么相同的名字会引起歧义。
  • 没有对传递的字段进行校验处理,如果传递了不在集合内的自定义列,那么势必会引发错误。
  • 还有其他没考虑到的等等问题。

总之,如果要实现极其完善的自定义列导出,那么这个简单的代码是肯定不够用的,需要从头开始设计一个兼容任何情况的自定义导出功能。


Excel 自定义列导出
http://aim467.github.io/2023/11/09/Excel-自定义列导出/
作者
Dedsec2z
发布于
2023年11月9日
更新于
2024年4月26日
许可协议