前段时间公司开发用到了excel导入导出的功能,于是我就想到了easyExcel ,今天做一个简单的记录
阿里出品用来处理Excel的开源工具
为什么要使用 easyexcel ?
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
【GitHub官网地址】
https://github.com/alibaba/easyexcel
官网文档:
https://alibaba-easyexcel.github.io/
【推荐文章】
阿里 EasyExcel 7 行代码优雅地实现 Excel 文件生成&下载功能
https://segmentfault.com/a/1190000019472781#articleHeader13
【maven依赖】
https://mvnrepository.com/artifact/com.alibaba/easyexcel
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>2.1.6version>
dependency>
【项目】
【初始化sql脚本】
执行脚本,创建初始数据
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '姓名',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`birthday` date DEFAULT NULL COMMENT '出生日期',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE =utf8mb4_unicode_ci;
INSERT INTO `user`
VALUES ('1', '张三', '23', '1996-10-23');
INSERT INTO `user`
VALUES ('2', '李四', '24', '1995-10-30');
【实体类 User】
package com.spiritmark.cloud.domain;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import java.util.Date;
@Data
public class User {
// 主键id
@ExcelIgnore // 生成报表时忽略,不生成次字段
private Integer id;
@ExcelProperty(value = "姓名", index = 0) // 定义表头名称和位置,0代表第一列
private String name;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
@ColumnWidth(20) // 定义列宽
@DateTimeFormat(value = "yyyy/MM/dd")
@ExcelProperty(value = "出生日期", index = 2)
private Date birthday;
}
【工具类】
package com.spiritmark.cloud.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
/**
* Excel工具类
*/
@Slf4j
public class ExcelUtils {
/**
* 导出Excel(07版.xlsx)到指定路径下
*
* @param path 路径
* @param excelName Excel名称
* @param sheetName sheet页名称
* @param clazz Excel要转换的类型
* @param data 要导出的数据
*/
public static void export2File(String path, String excelName, String sheetName, Class clazz, List data) {
String fileName = path.concat(excelName).concat(ExcelTypeEnum.XLSX.getValue());
EasyExcel.write(fileName, clazz).sheet(sheetName).doWrite(data);
}
/**
* 导出Excel(07版.xlsx)到web
*
* @param response 响应
* @param excelName Excel名称
* @param sheetName sheet页名称
* @param clazz Excel要转换的类型
* @param data 要导出的数据
* @throws Exception
*/
public static void export2Web(HttpServletResponse response, String excelName, String sheetName, Class clazz, List data) throws Exception {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
excelName = URLEncoder.encode(excelName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
}
/**
* 将指定位置指定名称的Excel导出到web
*
* @param response 响应
* @param path 文件路径
* @param excelName 文件名称
* @return
* @throws UnsupportedEncodingException
*/
public static String export2Web4File(HttpServletResponse response, String path, String excelName) throws UnsupportedEncodingException {
File file = new File(path.concat(excelName).concat(ExcelTypeEnum.XLSX.getValue()));
if (!file.exists()) {
return "文件不存在!";
}
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
excelName = URLEncoder.encode(excelName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());
try (
FileInputStream in = new FileInputStream(file);
ServletOutputStream out = response.getOutputStream();
) {
IOUtils.copy(in, out);
return "导出成功!";
} catch (Exception e) {
log.error("导出文件异常:", e);
}
return "导出失败!";
}
}
【调用】
package com.spiritmark.cloud.controller;
import com.alibaba.excel.EasyExcel;
import com.spiritmark.cloud.annotation.LogAnnotation;
import com.spiritmark.cloud.domain.User;
import com.spiritmark.cloud.listener.UserDataListener;
import com.spiritmark.cloud.service.UserService;
import com.spiritmark.cloud.util.ExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Slf4j
@Controller
@RequestMapping("/user")
public class UserController {
// 在application.yml中配置
@Value("${excel.path}")
private String path;
@Autowired
private UserService userService;
@RequestMapping("/toUserListPage")
public String toUserListPage() {
return "user/userList";
}
@LogAnnotation(description = "获取所有用户信息", clazz = User.class)
@RequestMapping("/getAll")
@ResponseBody
public List<User> getAll(User user) {
return userService.getAll();
}
// easyexcel导出Excel到指定位置
@GetMapping("/export2File")
@ResponseBody
public String export2File() {
ExcelUtils.export2File(path, "用户表", "用户信息", User.class, userService.getAll());
return "导出成功";
}
// easyexcel导出Excel到web
@GetMapping("/export2Web")
public void export2Web(HttpServletResponse response) {
try {
ExcelUtils.export2Web(response, "用户表", "用户信息", User.class, userService.getAll());
} catch (Exception e) {
log.error("报表导出异常:", e);
}
}
// 将指定位置指定名称的Excel导出到web
@GetMapping("/export2Web4File/{excelName}")
@ResponseBody
public String export2Web4File(HttpServletResponse response, @PathVariable String excelName) {
try {
return ExcelUtils.export2Web4File(response, path, excelName);
} catch (Exception e) {
log.error("文件导出异常:", e);
}
return "文件导出失败";
}
// easyexcel读取文件
@GetMapping("/read4File")
@ResponseBody
public String read4File() {
String fileName = path + "用户表导入.xlsx";
EasyExcel.read(fileName, User.class, new UserDataListener(userService)).sheet().doRead();
return "读取成功";
}
// 跳转到上传页面
@RequestMapping("/toUploadPage")
public String toUploadPage() {
return "user/upload";
}
// easyexcel上传文件
@PostMapping("/upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), User.class, new UserDataListener(userService)).sheet().doRead();
return "上传成功";
}
}
【UserDataListener】
package com.spiritmark.cloud.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.spiritmark.cloud.domain.User;
import com.spiritmark.cloud.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class UserDataListener extends AnalysisEventListener<User> {
private UserService userService;
public UserDataListener(UserService userService) {
this.userService = userService;
}
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<User> list = new ArrayList<User>();
@Override
public void invoke(User data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", list.size());
if (!CollectionUtils.isEmpty(list)) {
userService.saveBatch(list);
}
log.info("存储数据库成功!");
}
}
【测试】
【1】导出Excel到指定位置
http://localhost:8080/user/export2File
【2】导出Excel到web
http://localhost:8080/user/export2Web
http://localhost:8080/user/export2Web4File/{excelName}
例: http://localhost:8080/user/export2Web4File/ 用户表导入
http://localhost:8080/user/read4File