实现EasyExcel的导入导出(浏览器下载)
实现三个按钮的功能,但是却花费了一天的时间包括总结。
使用到的技术:springboot
layui
axios
EasyExcel
mybatis-plus
上传模板
不需要用到后端的交互,只需要前段<a>
即可
<a href="../static/excel/用户信息上传模板.xlsx" style="color:white">上传模版</a>
参考链接:
Excel如何对某一列设置下拉选择项 https://jingyan.baidu.com/article/f7ff0bfccae0e62e26bb1380.html
下载上传模板 https://www.bilibili.com/video/BV1dQ4y1A75e?from=search&seid=16041556233389040505
导入数据
使用到EasyExcel
的读Excel
后端
引入pom.xml 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDataExcel {
@ExcelProperty("用户名")
private String userName;
@ExcelProperty("密码")
private String password;
@ExcelProperty("姓名")
private String name;
@ExcelProperty("联系方式")
private String phone;
@ExcelProperty("用户类型")
private String typeStr;//取得的名字和User不一样,否则BeanUtils.copyProperties报错
@ExcelProperty("备注")
private String remark;
}
监听器
public class UserDataExcelListener extends AnalysisEventListener<UserDataExcel> {
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<User> list = new ArrayList<>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private UserService userService;
public UserDataExcelListener() {
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*/
public UserDataExcelListener(UserService userService ) {
this.userService = userService;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
//一行行读取
@Override
public void invoke(UserDataExcel data, AnalysisContext context) {
if(data==null){
throw new DormitoryException(ResultCode.ERROR,"文件数据为空");
}
//进行数据的转换 第一个参数复制到第二个参数中
User user = new User();
BeanUtils.copyProperties(data,user);
if(!StringUtils.isEmpty(data.getTypeStr())){
switch (data.getTypeStr()){
case "管理员":
user.setType(0);
break;
case "宿管员":
user.setType(1);
break;
case "学生":
user.setType(2);
break;
}
}
System.out.println("将excel的data复制到user,user:"+user);
list.add(user);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
}
/**
* 加上存储数据库 在监听器中直接添加
*/
private void saveData() {
userService.saveBatch(list);
}
}
UserController 控制层
@PostMapping("addUsers")
public Result addUsers(@RequestParam("uploadFile") MultipartFile file) throws IOException {
if(file==null){
System.out.println("文件为空");
}
boolean save = userService.addUsers(file);
return save ? Result.ok().mesaage("新增用户成功"):Result.error().mesaage("新增用户失败");
}
UserServiceImpl 业务实现层
需要注意的点,因为监听器没有Spring进行管理,所以这边的处理方式是将UserService
传入进去
@Override
public boolean addUsers(MultipartFile file) {
try{
InputStream in = file.getInputStream();
EasyExcel.read(in, UserDataExcel.class, new UserDataExcelListener(this)).sheet().doRead();
}catch (Exception e){
e.printStackTrace();
}
return true;
}
经过上面的步骤后,我们就可以先在apipost
中测试接口
apipost
测试MultipartFile
下面有几个注意点需要注意
前端
<button class="layui-btn layui-btn-sm " lay-event="importData" style="background: #2ecc71" > 导入数据 </button>
使用到了layui
的上传组件,由于使用到axios
,也简单地修改了一下upload
的源码中的axios
upload.render({
elem:'#importExcel',
url:'/user/addUsers',
size : '5000',//文件最大可允许上传的大小,单位 KB
accept:'file',//允许上传文件
exts:'xls|xlsx|xlsm|xlt|xltx|xltm',
field:'uploadFile',
headers: {token: store.getToken()},
done:function (result) {//执行上传请求后的回调。返回三个参数,分别为:res(服务端响应信息)、index(当前文件的索引)、upload(重新上传的方法,一般在文件上传失败后使用)
console.log(result);
if(result.code==0){
layer.msg("Excel导入数据成功",{
},function () {
table.reload("user-table-id");
})
}else{
layer.msg("Excel导入数据失败",function () {
})
}
}
});
小插曲的Bug,写在下面这篇博客中。
layui在toolbar使用上传控件在reload后失效的问题解决
参考链接 :
java+layui实现Excel的导入导出 https://www.cnblogs.com/bbllw/p/10800161.html
EasyExcel
文档 https://www.yuque.com/easyexcel/doc/easyexcel
课程上传的例子 https://www.bilibili.com/video/BV1dQ4y1A75e?from=search&seid=16041556233389040505
layui的upload文档说明 https://www.layui.com/doc/modules/upload.html
导出全部
使用到EasyExcel
的写Excel,这一点也是最难的一点,主要是卡在文件在浏览器中下载。
如果直接使用EasyExcel
中简单写的例子,只能将xlsx
文件下载到后端的项目文件中,不能在浏览器中下载。
后端
@GetMapping("exportAll")
public void exportAll(HttpServletResponse response) throws IOException {
List<UserDataExcel> listExcel = new ArrayList<>();
List<User> list = userService.list();
for(int i = 0;i<list.size();i++){
UserDataExcel dataExcel = new UserDataExcel();
BeanUtils.copyProperties(list.get(i),dataExcel);//小技巧
if(list.get(i).getType()!=null){//处理数据库中存放int 不是String
switch (list.get(i).getType()){
case 0:
dataExcel.setTypeStr("管理员");
break;
case 1:
dataExcel.setTypeStr("宿管员");
break;
case 2:
dataExcel.setTypeStr("学生");
break;
}
}
listExcel.add(dataExcel);
}
//文件名需要这样写,不能在setHeader直接写中文名,否则下载的文件名字为空,只有后缀
String fileName = new String("用户信息.xlsx".getBytes(), StandardCharsets.ISO_8859_1);
response.setContentType("application/msexcel");
response.setCharacterEncoding("utf8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName );
EasyExcel.write(response.getOutputStream(), UserDataExcel.class)
.sheet("sheet")
.doWrite(listExcel);
// return Result.ok().mesaage("下载成功"); 不要写
}
如果加了return Result.ok().mesaage("下载成功");
后端会报错,但是还是可以正常下载。
org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.dj.dormitory.commonutils.Result] with preset Content-Type 'application/msexcel;charset=utf8'
前端
<button class="layui-btn layui-btn-sm " lay-event="exportData" style="background: #27ae60"> 导出全部 </button>
刚开始的时候,自己想直接使用windows.open('/接口地址')
,但是因为项目中用到token,所以就放弃了这个方法。使用了下面的方法
layer.confirm('确定导出所有用户信息吗?', {
btn: ['确定', '取消']
}, function(index){
//window.open("http://localhost:8888/dormitory/user/exportAll");
axios({
method: 'get',
url:'/user/exportAll',
responseType: 'blob', // 重要, 限制返回的数据结构为blob格式,方便前端做转换
}).then(data=>{
const link = document.createElement('a')
const blob = new Blob([data], { type: 'application/vnd.ms-excel' })
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
link.setAttribute('download','用户信息.xlsx')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
layer.close(index);
}, function(index){
//按钮【按钮二】的回调
});
参考链接:
学到了 web中的代码具体应该怎么写
EasyExcel
web中的写 https://www.yuque.com/easyexcel/doc/write#afb7324a
了解到window.open("/download");
,可以在浏览器中实现成功下载了
EasyExcel实现下载Excel(解决无法从浏览器下载问题)https://blog.csdn.net/ruanbigshuai/article/details/108554896
了解到window.open("/download");
最简单,但是不可以携带token
axios实现excel文件下载 https://blog.csdn.net/xuesheng1610748/article/details/83865679
成功帮助到自己的方法三,使用token并且在浏览器中实现成功下载了
Vue项目利用axios请求接口下载excel(附前后端代码) https://blog.csdn.net/asmallprogrammer/article/details/91440793