1、簡介
EasyExcel是一個基于Java的、快速、簡潔、解決大檔案記憶體溢出的Excel處理工具。
他能讓你在不用考慮性能、記憶體的等因素的情況下,快速完成Excel的讀、寫等功能。
EasyExcel基于POI進行封裝優化,降低記憶體使用,再大的excel也不會出現記憶體溢出,讓使用更加簡單友善。
官網: https://easyexcel.opensource.alibaba.com/
github: https://github.com/alibaba/easyexcel
gitee:https://gitee.com/easyexcel/easyexcel
2、讀取Excel
EasyExcel需引入如下依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.0</version>
</dependency>
示例中還需引入其他依賴:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
示例Excel表格demo1.xlsx如下:
讀取Excel資料的對象:
@Data
@HeadRowHeight(20)
@ColumnWidth(20)
public class DemoData {
@ExcelProperty("字元串标題")
private String string;
@ExcelProperty("日期标題")
private Date date;
@ExcelProperty("數字标題")
private Double doubleData;
@ExcelIgnore
private String ignore;
}
ExcelProperty注解: 用于比對excel和實體類;
ExcelIgnore注解:預設所有字段都會和excel去比對,加了這個注解會忽略該字段;
HeadRowHeight注解:标注在類上,表頭高
ColumnWidth注解:标注在類或字段上,列寬
@Slf4j
public class EasyExcelReadTest {
@Test
public void syncRead() {
String fileName = "demo1.xlsx";
// 這裡需要指定讀用哪個class去讀,然後讀取第一個sheet 同步讀取會自動finish
List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
for (DemoData data : list) {
log.info("讀取到資料:{}", JSON.toJSONString(data));
}
// 這裡也可以不指定class,傳回一個list,然後讀取第一個sheet 同步讀取會自動finish
List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
for (Map<Integer, String> data : listMap) {
// 傳回每條資料的鍵值對 表示所在的列 和所在列的值
log.info("讀取到資料:{}", JSON.toJSONString(data));
}
}
/**
* 最簡單的讀
*/
@Test
public void simpleRead() {
// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
// 寫法1:
String fileName = "demo1.xlsx";
// 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet,檔案流會自動關閉
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
// 寫法2:
try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) {
// 建構一個sheet 這裡可以指定名字或者no
ReadSheet readSheet = EasyExcel.readSheet(0).build();
// 讀取一個sheet
excelReader.read(readSheet);
}
}
}
讀監聽器:
/**
* 模闆的讀取類
*
* 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裡面用到spring可以構造方法傳進去
*/
@Slf4j
public class DemoDataListener extends AnalysisEventListener<DemoData> {
/**
* 每隔5條存儲資料庫,實際使用中可以1000條,然後清理list,友善記憶體回收
*/
private static final int BATCH_COUNT = 5;
private List<DemoData> list = new ArrayList<>();
private DemoService demoService;
public DemoDataListener() {
// 這裡是demo,是以随便new一個。實際使用如果到了spring,請使用下面的有參構造函數
demoService = new DemoService();
}
public DemoDataListener(DemoService demoService) {
this.demoService = demoService;
}
/**
* 這個每一條資料解析都會來調用
*
* @param data
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("解析到一條資料:{}", JSON.toJSONString(data));
list.add(data);
// 達到BATCH_COUNT了,需要去存儲一次資料庫,防止資料幾萬條資料在記憶體,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存儲完成清理 list
list.clear();
}
}
/**
* 所有資料解析完成了都會來調用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 這裡也要儲存資料,確定最後遺留的資料也存儲到資料庫
saveData();
log.info("所有資料解析完成!");
}
/**
* 加上存儲資料庫
*/
private void saveData() {
log.info("{}條資料,開始存儲資料庫!", list.size());
demoService.save(list);
log.info("存儲資料庫成功!");
}
}
業務層資料處理:
public class DemoService {
public void save(List<DemoData> list) {
// 這裡寫入資料庫
}
}
3、寫入Excel
@Slf4j
public class EasyExcelWriteTest {
@Test
public void writeExcel() {
EasyExcel.write("demo1.xlsx", DemoData.class).sheet("模闆").doWrite(data());
}
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字元串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
}
4、Web上傳下載下傳
@Controller
public class UploadDownController {
@Autowired
private DemoService demoService;
/**
* 檔案下載下傳(失敗了會傳回一個有部分資料的Excel)
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 這裡注意 有同學反應使用swagger 會導緻各種問題,請直接用浏覽器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 這裡URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關系
String fileName = URLEncoder.encode("測試", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DemoData.class).sheet("模闆").doWrite(data());
}
/**
* 檔案下載下傳并且失敗的時候傳回json(預設失敗了會傳回一個有部分資料的Excel)
*
* @since 2.1.1
*/
@GetMapping("downloadFailedUsingJson")
public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
// 這裡注意有同學反應使用swagger會導緻各種問題,請直接用浏覽器或者用postman
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 這裡URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關系
String fileName = URLEncoder.encode("測試", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 這裡需要設定不關閉流
EasyExcel.write(response.getOutputStream(), DemoData.class).autoCloseStream(Boolean.FALSE).sheet("模闆")
.doWrite(data());
} catch (Exception e) {
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>();
map.put("status", "failure");
map.put("message", "下載下傳檔案失敗" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
}
/**
* 檔案上傳
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), DemoData.class, new DemoDataListener(demoService)).sheet().doRead();
return "success";
}
private List<DemoData> data() {
List<DemoData> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字元串" + 0);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
}
Excel上傳時,擷取Excel資料,我們也可以封裝個工具類ExcelUtils:
@Slf4j
public class ExcelUtils {
public static <T> List<T> getExcelModelData(final InputStream inputStream, Class<T> clazz) {
if (null == inputStream) {
throw new NullPointerException("the inputStream is null!");
}
ExcelReaderBuilder result = EasyExcel.read(inputStream, clazz, null);
ExcelReaderSheetBuilder sheet1 = result.sheet();
List<T> resultData = sheet1.doReadSync();
return resultData;
}
}
上傳的代碼也可改為如下:
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
List<DemoData> excelModelData = ExcelUtils.getExcelModelData(file.getInputStream(), DemoData.class);
demoService.save(excelModelData);
return "success";
}
其他示例可參考:
EasyExcel Demo: https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read
API參考:
EasyExcel API: https://easyexcel.opensource.alibaba.com/docs/current/api/
原文連結:https://river106.cn/posts/1e3d4153.html