前后端分离项目中-EasyExcel导入和导出
添加依赖
创建SpringBoot工程,我这里使用的SpringBoot是2.7.16
添加需要的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
我这里使用的easyexcel版本是3.3.2,是我在写这篇文章的时候的最新版本。
导入操作
流程说明
导入Excel的操作的流程应该是:
①上传Excel文件。
②后端接收到文件之后,解析excel文件中的数据。
③拿到解析得到的数据之后,按需处理。
现在的处理是,文件上传还是传统的文件上传操作。EasyExcel帮我们完成了文件的解析,我们可以直接获取到解析后的数据。
代码示例
看看具体的代码和API说明吧:
第一步: 创建实体类
EasyExcel 可以帮我们将Excel表格中的转换为我们的实体类对象列表,所以我们需要定义一个实体类。
比如:
public class Student {
@ExcelProperty("编号")
private Long stuId;
@ExcelProperty("姓名")
private String stuName;
@ExcelProperty("生日")
private Date stuBirth;
@ExcelProperty("是否毕业")
private Boolean graduate;
@ExcelProperty("年龄")
private Integer age;
public Student() {
}
public Student(Long stuId, String stuName, Date stuBirth, Boolean graduate, Integer age) {
this.stuId = stuId;
this.stuName = stuName;
this.stuBirth = stuBirth;
this.graduate = graduate;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"stuId=" + stuId +
", stuName='" + stuName + '\'' +
", stuBirth=" + stuBirth +
", graduate=" + graduate +
", age=" + age +
'}';
}
说明
实体类成员变量上方的注解说明
- ExcelProperty 用于匹配excel和实体类的匹配,参数如下:
- value 默认为空,用于匹配excel中的头,必须全匹配,如果有多行头,会匹配最后一行头
- order 默认Integer.MAX_VALUE,优先级高于value,会根据order的顺序来匹配实体和excel中数据的顺序
- index 默认-1,优先级高于value和order,会根据index直接指定到excel中具体的哪一列
- ExcelIgnore 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
- ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与读写
- DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解,参数如下:
- value 默认空,参照java.text.SimpleDateFormat书写即可
- use1904windowing 默认自动选择,excel中时间是存储1900年起的一个双精度浮点数,但是有时候默认开始日期是1904,所以设置这个值改成默认1904年开始
- NumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解。
- value 默认空,参照java.text.DecimalFormat书写即可
- roundingMode 默认RoundingMode.HALF_UP,格式化的时候设置舍入模式
第二步:准备监听器处理解析的数据。
EasyExcel在解析excel文件并不是直接返回解析的数据,而是在解析过程中需要一个监听器来监听解析过程,每解析一条数据出来都会触发监听的对应的方法,并且将我们解析的数据传入到这个方法中,我们只需要在这个方法中处理这条数据即可。
看看我们的处理器:
@Component
@Scope("prototype")
public class StudentListener extends AnalysisEventListener<Student> {
@Override
public void invoke(Student student, AnalysisContext analysisContext) {
System.out.println("在listener中保存一行数据:" + student);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
第三步:准备controller,这个就简单了,直接上货
@RestController
@CrossOrigin
public class DemoController {
@Autowired
private StudentListener studentListener;
@RequestMapping("/upload")
public String upload(MultipartFile uploadFile){
try {
// 得到excel读取对象
ExcelReaderBuilder builder = EasyExcel.read(uploadFile.getInputStream(), Student.class,studentListener);
// 获取表格
ExcelReaderSheetBuilder sheet = builder.sheet();
// 读取数据
sheet.doRead();
} catch (IOException e) {
throw new RuntimeException(e);
}
return "ok";
}
}
第四步:前端,额~~这一部分的话按需处理极刻,我这里是ElementPlus,上货
<template>
<div>
<el-upload
v-model:file-list="fileList"
class="upload-demo"
action="http://localhost:8888/upload"
name="uploadFile"
:on-success="uploadSuccess"
:show-file-list="false"
>
<el-button type="primary">导入信息</el-button>
</el-upload>
<el-button @click="doExport" type="success">直接超链接导出</el-button>
<el-button @click="downloadExcel" type="success">axios导出</el-button>
</div>
</template>
OK,准备文件:
上传,搞定。
导出操作
思路说明
导出操作相当简单,我们准备好数据,然后将数据交给EasyExcel,EasyExcel自动将数据列表转换为Excel文件,我们按照传统文件下载的操作设置好相关的响应头内容,获得输出流,然后将输出流交个EasyExcel,就自动完成文件的导出操作了。
代码示例
实体类还是使用上一章节的实体类,这里我们只需要写好controller即可:
@RequestMapping("/download")
public void download(HttpServletResponse response) throws IOException {
// 设置头消息
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 文件名
String fileName = URLEncoder.encode("学员信息","utf-8");
// 设置响应头
response.addHeader("Content-Disposition","attachment;filename=" + fileName + ".xlsx");
response.addHeader("Access-Control-Expose-Headers","Content-Disposition");
// 得到输出流
ServletOutputStream out = response.getOutputStream();
// 得到Writer
ExcelWriterBuilder writer = EasyExcel.write(out,Student.class);
// 获得表格数据对象
ExcelWriterSheetBuilder sheet = writer.sheet();
// 准备输出的数据
List<Student> list = new ArrayList<>();
list.add(new Student(1L,"卡卡西",new Date(),false,31));
list.add(new Student(3L,"鸣人",new Date(),false,31));
list.add(new Student(12L,"佐助",new Date(),true,31));
// 写出数据
sheet.doWrite(list);
}
特别说明:
在前后端分离的项目中,默认只有部分的响应头消息可以响应到前端,我们代码中设置的Content-Disposition
是无法响应的,如果我们希望这个响应头到达前端,我们就需要做一些设置:response.addHeader("Access-Control-Expose-Headers","Content-Disposition");
前端的处理:
methods: {
uploadSuccess(res) {
console.log(res);
},
doExport() {
// 这里直接使用超链接完成下载
document.location.href = "http://localhost:8888/download";
},
// 有时候我们需要一个下载的动画,或者下载的反馈,这时可能就要考虑下面的方案了。
downloadExcel() {
axios.get("http://localhost:8888/download").then((res) => {
if (!res) {
return;
}
// 得到文件流后,前端生成文件,创建出a标签进行点击
const fileName = res.headers.get("Content-Disposition").split("=")[1];
const blob = new Blob([res.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8",
});
const url = window.URL.createObjectURL(blob);
const aLink = document.createElement("a");
aLink.style.display = "none";
aLink.href = url;
aLink.setAttribute("download", decodeURI(fileName));
document.body.appendChild(aLink);
aLink.click();
document.body.removeChild(aLink);
window.URL.revokeObjectURL(url);
});
},
},
完事!