SpringBoot中EasyExcel實現(xiàn)Excel文件的導入導出
前言
在我們?nèi)粘5拈_發(fā)過程中經(jīng)常會使用Excel文件的形式來批量地上傳下載系統(tǒng)數(shù)據(jù),我們最常用的工具是Apache poi,但是如果數(shù)據(jù)到底上百萬時,將會造成內(nèi)存溢出的問題,那么我們怎么去實現(xiàn)百萬數(shù)據(jù)批量導入導出。
正文
Easyexcel
Easyexcel 是阿里巴巴的開源項目,用來優(yōu)化Excel文件處理過程:
- poi消耗內(nèi)存嚴重:Java解析、生成
Excel比較有名的框架有Apache poi、jxl。但他們都存在一個嚴重的問題就是非常的耗內(nèi)存,poi有一套SAX模式的API可以一定程度的解決一些內(nèi)存溢出的問題,但poi還是有一些缺陷,比如07版Excel解壓縮以及解壓后存儲都是在內(nèi)存中完成的,內(nèi)存消耗依然很大。 - easyexcel針對內(nèi)存做出了優(yōu)化:重寫了
poi對07版Excel的解析,能夠原本一個3M的excel用POI sax依然需要100M左右內(nèi)存降低到幾M,并且再大的excel不會出現(xiàn)內(nèi)存溢出。

SpringBoot+ EasyExcel實現(xiàn)Excel文件的導入導出
導入依賴
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<optional>true</optional>
</dependency>
<!--easyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beat1</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
為了防止Excel文件被破壞在pom.xml添加以下內(nèi)容
<build>
<plugins>
<!-- 讓maven不編譯xls文件,但仍將其打包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>xls</nonFilteredFileExtension>
<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>
application.propertis:配置文件
#temp files project.tmp.files.path=/Users/mac/Desktop/image/tmp/files/
在SpringBoot啟動類添加臨時文件設置
@Value("${project.tmp.files.path}")
public String filesPath;
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
//設置路徑xxx
factory.setLocation(filesPath);
return factory.createMultipartConfig();
}
ExcelUtil:Excel工具類
@Slf4j
public class ExcelUtil {
private static Sheet initSheet;
static {
initSheet = new Sheet(1, 0);
initSheet.setSheetName("sheet");
//設置自適應寬度
initSheet.setAutoWidth(Boolean.TRUE);
}
public static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {
try {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
workbook.write(response.getOutputStream());
} catch (IOException e) {
// throw new NormalException(e.getMessage());
}
}
/**
* 讀取少于1000行數(shù)據(jù)
*
* @param filePath 文件絕對路徑
* @return
*/
public static List<Object> readLessThan1000Row(String filePath) {
return readLessThan1000RowBySheet(filePath, null);
}
/**
* 讀小于1000行數(shù)據(jù), 帶樣式
* filePath 文件絕對路徑
* initSheet :
* sheetNo: sheet頁碼,默認為1
* headLineMun: 從第幾行開始讀取數(shù)據(jù),默認為0, 表示從第一行開始讀取
* clazz: 返回數(shù)據(jù)List<Object> 中Object的類名
*/
public static List<Object> readLessThan1000RowBySheet(String filePath, Sheet sheet) {
if (!StringUtils.hasText(filePath)) {
return null;
}
sheet = sheet != null ? sheet : initSheet;
InputStream fileStream = null;
try {
fileStream = new FileInputStream(filePath);
return EasyExcelFactory.read(fileStream, sheet);
} catch (FileNotFoundException e) {
log.info("找不到文件或文件路徑錯誤, 文件:{}", filePath);
} finally {
try {
if (fileStream != null) {
fileStream.close();
}
} catch (IOException e) {
log.info("excel文件讀取失敗, 失敗原因:{}", e);
}
}
return null;
}
/**
* 讀大于1000行數(shù)據(jù)
*
* @param filePath 文件覺得路徑
* @return
*/
public static List<Object> readMoreThan1000Row(String filePath) {
return readMoreThan1000RowBySheet(filePath, null);
}
/**
* 讀大于1000行數(shù)據(jù), 帶樣式
*
* @param filePath 文件覺得路徑
* @return
*/
public static List<Object> readMoreThan1000RowBySheet(String filePath, Sheet sheet) {
if (!StringUtils.hasText(filePath)) {
return null;
}
sheet = sheet != null ? sheet : initSheet;
InputStream fileStream = null;
try {
fileStream = new FileInputStream(filePath);
ExcelListener excelListener = new ExcelListener();
EasyExcelFactory.readBySax(fileStream, sheet, excelListener);
return excelListener.getDatas();
} catch (FileNotFoundException e) {
log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath);
} finally {
try {
if (fileStream != null) {
fileStream.close();
}
} catch (IOException e) {
log.error("excel文件讀取失敗, 失敗原因:{}", e);
}
}
return null;
}
/**
* 讀大于1000行數(shù)據(jù), 帶樣式
*
* @return
*/
public static List<Object> readMoreThan1000RowBySheetFromInputStream(InputStream inputStream, Sheet sheet) {
sheet = sheet != null ? sheet : initSheet;
InputStream fileStream = null;
ExcelListener excelListener = new ExcelListener();
EasyExcelFactory.readBySax(inputStream, sheet, excelListener);
return excelListener.getDatas();
}
/**
* 生成excle
*
* @param filePath 絕對路徑
* @param data 數(shù)據(jù)源
* @param head 表頭
*/
public static void writeBySimple(String filePath, List<List<Object>> data, List<String> head) {
writeSimpleBySheet(filePath, data, head, null);
}
/**
* 生成excle
*
* @param filePath 路徑
* @param data 數(shù)據(jù)源
* @param sheet excle頁面樣式
* @param head 表頭
*/
public static void writeSimpleBySheet(String filePath, List<List<Object>> data, List<String> head, Sheet sheet) {
sheet = (sheet != null) ? sheet : initSheet;
if (head != null) {
List<List<String>> list = new ArrayList<>();
head.forEach(h -> list.add(Collections.singletonList(h)));
sheet.setHead(list);
}
OutputStream outputStream = null;
ExcelWriter writer = null;
try {
outputStream = new FileOutputStream(filePath);
writer = EasyExcelFactory.getWriter(outputStream);
writer.write1(data, sheet);
} catch (FileNotFoundException e) {
log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath);
} finally {
try {
if (writer != null) {
writer.finish();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
log.error("excel文件導出失敗, 失敗原因:{}", e);
}
}
}
/**
* 生成excle
*
* @param filePath 路徑
* @param data 數(shù)據(jù)源
*/
public static void writeWithTemplate(String filePath, List<? extends BaseRowModel> data) {
writeWithTemplateAndSheet(filePath, data, null);
}
/**
* 生成excle
*
* @param filePath 路徑
* @param data 數(shù)據(jù)源
* @param sheet excle頁面樣式
*/
public static void writeWithTemplateAndSheet(String filePath, List<? extends BaseRowModel> data, Sheet sheet) {
if (CollectionUtils.isEmpty(data)) {
return;
}
sheet = (sheet != null) ? sheet : initSheet;
sheet.setClazz(data.get(0).getClass());
OutputStream outputStream = null;
ExcelWriter writer = null;
try {
outputStream = new FileOutputStream(filePath);
writer = EasyExcelFactory.getWriter(outputStream);
writer.write(data, sheet);
} catch (FileNotFoundException e) {
log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath);
} finally {
try {
if (writer != null) {
writer.finish();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
log.error("excel文件導出失敗, 失敗原因:{}", e);
}
}
}
/**
* 生成多Sheet的excle
*
* @param filePath 路徑
* @param multipleSheelPropetys
*/
public static void writeWithMultipleSheel(String filePath, List<MultipleSheelPropety> multipleSheelPropetys) {
if (CollectionUtils.isEmpty(multipleSheelPropetys)) {
return;
}
OutputStream outputStream = null;
ExcelWriter writer = null;
try {
outputStream = new FileOutputStream(filePath);
writer = EasyExcelFactory.getWriter(outputStream);
for (MultipleSheelPropety multipleSheelPropety : multipleSheelPropetys) {
Sheet sheet = multipleSheelPropety.getSheet() != null ? multipleSheelPropety.getSheet() : initSheet;
if (!CollectionUtils.isEmpty(multipleSheelPropety.getData())) {
sheet.setClazz(multipleSheelPropety.getData().get(0).getClass());
}
writer.write(multipleSheelPropety.getData(), sheet);
}
} catch (FileNotFoundException e) {
log.error("找不到文件或文件路徑錯誤, 文件:{}", filePath);
} finally {
try {
if (writer != null) {
writer.finish();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
log.error("excel文件導出失敗, 失敗原因:{}", e);
}
}
}
/*********************匿名內(nèi)部類開始,可以提取出去******************************/
@Data
public static class MultipleSheelPropety {
private List<? extends BaseRowModel> data;
private Sheet sheet;
}
/**
* 解析監(jiān)聽器,
* 每解析一行會回調(diào)invoke()方法。
* 整個excel解析結束會執(zhí)行doAfterAllAnalysed()方法
*
* @author: chenmingjian
* @date: 19-4-3 14:11
*/
@Getter
@Setter
public static class ExcelListener extends AnalysisEventListener {
private List<Object> datas = new ArrayList<>();
/**
* 逐行解析
* object : 當前行的數(shù)據(jù)
*/
@Override
public void invoke(Object object, AnalysisContext context) {
//當前行
// context.getCurrentRowNum()
if (object != null) {
datas.add(object);
}
}
/**
* 解析完所有數(shù)據(jù)后會調(diào)用該方法
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//解析結束銷毀不用的資源
}
}
/************************匿名內(nèi)部類結束,可以提取出去***************************/
}
CommonUtil:工具類
public class CommonUtil {
/**
* 生成32位編碼,不含橫線
*
* @return uuid串
*/
public static String getUUID() {
String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
return uuid.toUpperCase();
}
/**
* 得到當前日期格式化后的字符串,格式:yyyy-MM-dd(年-月-日)
* @return 當前日期格式化后的字符串
*/
public static String getTodayStr(){
return new SimpleDateFormat("yyyy-MM-dd").format(new Date()) ;
}
/**
* 將對象轉化成json
*
* @param t
* @return
* @throws JsonProcessingException
*/
public static <T> String toJson(T t) throws JsonProcessingException {
return OBJECT_MAPPER.get().writeValueAsString(t);
}
}
UserPojoRes:實體類
@Setter
@Getter
@ToString
public class UserPojoRes extends BaseRowModel implements Serializable {
private static final long serialVersionUID = -2145503717390503506L;
/**
* 主鍵
*/
@ExcelProperty(value = "ID", index = 0)
private String id;
/**
* 姓名
*/
@ExcelProperty(value = "用戶名", index = 1)
private String name;
public UserPojoRes(String id, String name) {
this.id = id;
this.name = name;
}
public UserPojoRes(){
}
}
驗證
模板下載
這里將模板文件放在resources中
@GetMapping("/exportExcelTempalte")
@ApiOperation(value = "下載導入模板")
public void exportExcelTempalte(HttpServletResponse response) throws Exception {
//Resource目錄中的文件
String filePath = "/excels/導入模板.xlsx";
ClassPathResource classPathResource = new ClassPathResource(filePath);
Workbook workbook=WorkbookFactory.create(classPathResource.getInputStream());
ExcelUtil.downLoadExcel("導入模板.xlsx", response, workbook);
}
Excel文件導入
@PostMapping("/importExcel")
@ApiOperation(value = "Excel文件導入")
public Response importExcel(HttpServletRequest request, MultipartFile file, HttpServletResponse response) throws Exception {
List<Object> objects = ExcelUtil.readMoreThan1000RowBySheetFromInputStream(file.getInputStream(),null);
List<UserPojoRes> list = new ArrayList<>();
for (Object o : objects) {
UserPojoRes userPojoRes = new UserPojoRes();
List<String> stringList = (List<String>) o;
userPojoRes.setId(stringList.get(0) != null ? stringList.get(0).toString() : "");
userPojoRes.setName(stringList.get(1) != null ? stringList.get(0).toString() : "");
list.add(userPojoRes);
}
String json = CommonUtil.toJson(list);
return new Response(json);
}

Excel文件導出
@Value("${project.tmp.files.path}")
public String filesPath;
@GetMapping("/exportExcel")
@ApiOperation(value = "Excel文件導出")
public void exportExcel(HttpServletResponse response) throws Exception {
//創(chuàng)建臨時文件
String path = filesPath + CommonUtil.getUUID() + ".xlsx";
List<UserPojoRes> list = new ArrayList<>();
UserPojoRes userPojoRes = new UserPojoRes("009", "張三");
UserPojoRes userPojoRes1 = new UserPojoRes("009", "李四");
list.add(userPojoRes);
list.add(userPojoRes1);
ExcelUtil.writeWithTemplate(path, list);
// 根據(jù)excel創(chuàng)建對象
Workbook workbook = WorkbookFactory.create(new FileInputStream(path));
String fileName = "用戶模塊" + CommonUtil.getTodayStr() + ".xlsx";
ExcelUtil.downLoadExcel(fileName, response, workbook);
}

到此這篇關于SpringBoot中EasyExcel實現(xiàn)Excel文件的導入導出的文章就介紹到這了,更多相關Java Excel文件導入導出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot 導出數(shù)據(jù)生成excel文件返回方式
- SpringBoot?整合?EasyExcel?實現(xiàn)自由導入導出功能
- SpringBoot整合EasyExcel實現(xiàn)批量導入導出
- springboot實現(xiàn)excel表格導出幾種常見方法
- SpringBoot整合EasyExcel實現(xiàn)文件導入導出
- 使用VUE+SpringBoot+EasyExcel?整合導入導出數(shù)據(jù)的教程詳解
- SpringBoot+EasyPoi實現(xiàn)excel導出功能
- SpringBoot導出Excel的四種實現(xiàn)方式
- springboot實現(xiàn)對接poi 導出excel折線圖
相關文章
java serialVersionUID解決序列化類版本不一致問題面試精講
這篇文章主要為大家介紹了serialVersionUID解決序列化類版本不一致問題的面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
java調(diào)用js文件的兩種常用方法示例(支持V8引擎)
在Java中調(diào)用JavaScript的方法通常涉及到使用Java的腳本引擎,下面這篇文章主要給大家介紹了關于java調(diào)用js文件的兩種常用方法(支持V8引擎)的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-06-06
Springboot Thymeleaf模板文件調(diào)用Java類靜態(tài)方法
這篇文章主要介紹了Springboot Thymeleaf模板文件調(diào)用Java類靜態(tài)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2007-09-09
Seata AT模式TransactionHook被刪除探究
這篇文章主要為大家介紹了Seata AT模式TransactionHook被刪除探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
mybatis-plus查詢無數(shù)據(jù)問題及解決
這篇文章主要介紹了mybatis-plus查詢無數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
Java Spring 中 @PostConstruct 注解使用原理及常見場景
在 Java Spring 中,@PostConstruct 注解是一個非常實用的功能,它允許開發(fā)者在 Spring 容器完全初始化 Bean 后執(zhí)行某些操作,本文將詳細介紹 @PostConstruct 注解的使用,包括其原理、常見場景和代碼示例,2025-04-04

