前段時間公司開發用到了excel導入導出的功能,於是我就想到了easyExcel ,今天做一個簡單的記錄
【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
所以本文demo使用2.1.6版本
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</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
【3】將指定位置指定名稱的Excel導出到web
http://localhost:8080/user/export2Web4File/{excelName}
例: http://localhost:8080/user/export2Web4File/ 用戶表導入
【4】easyexcel讀取文件
http://localhost:8080/user/read4File
處理邏輯是將Excel中的數據保存到數據庫中
導入前數據庫數據
選擇要導入的文件
點擊“提交”,把數據保存到數據庫
OK,大功告成