EasyExcel实现文件读取、导出、上传、下载操作
?一、EasyExcel簡介
? ? ? Java解析、生成Excel比較有名的框架有Apache poi、jxl。但他們都存在一個嚴(yán)重的問題就是非常的耗內(nèi)存,poi有一套SAX模式的API可以一定程度的解決一些內(nèi)存溢出的問題,但POI還是有一些缺陷,比如07版Excel解壓縮以及解壓后存儲都是在內(nèi)存中完成的,內(nèi)存消耗依然很大。easyexcel重寫了poi對07版Excel的解析,能夠原本一個3M的excel用POI sax依然需要100M左右內(nèi)存降低到幾M,并且再大的excel不會出現(xiàn)內(nèi)存溢出,03版依賴POI的sax模式。在上層做了模型轉(zhuǎn)換的封裝,讓使用者更加簡單方便
二、使用方法
?1.引入依賴
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.8</version> </dependency>2.讀取excel
(1)默認(rèn)方式讀取
通過EasyExcel.read()方式進(jìn)行,參數(shù)依次為文件路徑、要讀取成指定文件的類、讀取文件類的監(jiān)聽器,sheet中默認(rèn)讀取第一個sheet,也可以自己制定,讀取文件時從第二行開始,可以按批次讀取,也可以自定義讀取
@Test public void easyExcelRead(){String path = "D:\\city_district.xlsx";EasyExcel.read(path, TeleCity.class, new TeleCityListener(teleCityService)).sheet("city市區(qū)表").doRead(); }監(jiān)聽器TeleCityListener,這里是繼承了AnalysisEventListener,需要指定讀取的類,因為沒有被spring管理,所以這里需要new TeleCityListener(teleCityService),傳入的teleCityService需要注入,方式來創(chuàng)建實例,通過構(gòu)造器方式傳入注入的dao或者service來賦值,調(diào)用相應(yīng)的方法,插入數(shù)據(jù)庫,也可以使用默認(rèn)構(gòu)造器,不會對數(shù)據(jù)庫進(jìn)行操作
@Slf4j public class TeleCityListener extends AnalysisEventListener<TeleCity> {/*** 假設(shè)這個是一個DAO,當(dāng)然有業(yè)務(wù)邏輯這個也可以是一個service。當(dāng)然如果不用存儲這個對象沒用。*/private TeleCityService teleCityService;/*** 如果使用了spring,請使用這個構(gòu)造方法。每次創(chuàng)建Listener的時候需要把spring管理的類傳進(jìn)來** @param teleCityService*/public TeleCityListener(TeleCityService teleCityService) {this.teleCityService = teleCityService;}public TeleCityListener(){}/*** 每隔100條存儲數(shù)據(jù)庫,實際使用中可以3000條,然后清理list ,方便內(nèi)存回收*/private static final int BATCH_COUNT = 100;List<TeleCity> list = new ArrayList<TeleCity>();/*** 這個每一條數(shù)據(jù)解析都會來調(diào)用** @param teleCity* ? ? ? ? ? ?one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param analysisContext*/@Overridepublic void invoke(TeleCity teleCity, AnalysisContext analysisContext) {log.info("解析到一條數(shù)據(jù):{}", JSON.toJSONString(teleCity));list.add(teleCity);// 達(dá)到BATCH_COUNT了,需要去存儲一次數(shù)據(jù)庫,防止數(shù)據(jù)幾萬條數(shù)據(jù)在內(nèi)存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存儲完成清理 listlist.clear();}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}/*** 加上存儲數(shù)據(jù)庫*/private void saveData() {log.info("{}條數(shù)據(jù),開始存儲數(shù)據(jù)庫!", list.size());teleCityService.insertTeleCityList(list);log.info("存儲數(shù)據(jù)庫成功!");} }(2)使用自定義的通用監(jiān)聽器讀取文件
通過泛型和繼承AnalysisEventListener來自定義通用的一個讀取監(jiān)聽器,只讀取數(shù)據(jù),讀到的數(shù)據(jù)全都封裝在list里面,通過getList()即可得到返回的數(shù)據(jù),想要處理哪種格式就傳入相應(yīng)的類即可
@Slf4j public class GeneralListener<T> extends AnalysisEventListener<T> {private List<T> list= Lists.newArrayList();@Overridepublic void invoke(T data, AnalysisContext context) {Assert.notNull(data,"導(dǎo)入數(shù)據(jù)不能為null");log.info("start read list of data :{}",JSON.toJSONString(data));list.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {log.info("read data complete!");}public List<T> getList() {return list;} }創(chuàng)建一個實例,讀取完畢后,調(diào)用getlist()方法
@Test public void generalExcelRead(){String path = "D:\\city_district.xlsx";//建立一個通用讀取監(jiān)聽器,讀取數(shù)據(jù)到這個里面,通過getList方式獲取讀取的數(shù)據(jù)GeneralListener<TeleCity> teleListener=new GeneralListener<>();//EasyExcel進(jìn)行讀取try{EasyExcel.read(path,TeleCity.class,teleListener).sheet("city市區(qū)表").doRead();}catch (Exception e){log.error("讀取數(shù)據(jù)失敗!",e);}log.info("讀取到的數(shù)據(jù)大小為:{}", JSON.toJSONString(teleListener.getList().size()));//進(jìn)行數(shù)據(jù)庫插入操作teleCityService.insertTeleCityList(teleListener.getList()); }3.導(dǎo)出文件為Excel
使用EasyExcel.write()即可,里面參數(shù)為文件路徑,要轉(zhuǎn)出的文件內(nèi)容,和sheet名稱以及要導(dǎo)出的數(shù)據(jù)
@Test public void generateExcelWrite(){List<TeleCity> list=teleCityService.getList();List<TeleCityExcelDTO> exportList=JSON.parseArray(JSON.toJSONString(list),TeleCityExcelDTO.class);log.info("開始寫入數(shù)據(jù)!");String path = "D:\\demo.xlsx";try {EasyExcel.write(path, TeleCityExcelDTO.class).sheet("模板").doWrite(exportList);}catch (Exception e){log.error("寫入數(shù)據(jù)失敗!",e);}log.error("寫入數(shù)據(jù)成功!大小為{}", JSON.toJSONString(list.size())); }定義導(dǎo)出的數(shù)據(jù)文件格式:通過注釋來指定哪些列不需要導(dǎo)出,導(dǎo)出列位置和名稱以及相應(yīng)的高度、寬度等
@Data public class TeleCityExcelDTO implements Serializable {/*** 忽略不讀取和寫入*/@ExcelIgnoreprivate static final long serialVersionUID = 8372588291576645501L;/*** 強制讀取第三個 這里不建議 index 和 name 同時用,要么一個對象只用index,要么一個對象只用name去匹配*/@ExcelProperty(index = 0,value = "id")@ColumnWidth(10)private Long id;@ExcelProperty(index = 1,value = "城市名稱")@ColumnWidth(20)private String cityName;@ExcelProperty(index = 2,value = "組織編碼")@ColumnWidth(20)private String orgCode;@ExcelProperty(index = 3,value = "物流編碼")@ColumnWidth(20)private String logisticCode;@ExcelProperty(index = 4,value = "省份Id")@ColumnWidth(10)private Long provinceId;@ExcelProperty(index = 5,value = "創(chuàng)建時間")@ColumnWidth(20)private String createTime;@ExcelProperty(index = 6,value = "更新時間")@ColumnWidth(20)private String updateTime; }4.Download下載文件
類似于上面導(dǎo)出文件,需要自定義導(dǎo)出文件內(nèi)容格式。實際是將導(dǎo)出的文件寫入response中,需要指定編碼格式和輸出流格式等。這里特別注意是需要使用Get方式請求的,將下載的數(shù)據(jù)在當(dāng)前界面作為附件方式下載,不會打開新的界面
?@GetMapping("/test17")public void testExportExcel(HttpServletResponse response) {//導(dǎo)出數(shù)據(jù)List<TeleCity> list = teleCityService.getList();List<TeleCityExcelDTO> exportList = JSON.parseArray(JSON.toJSONString(list), TeleCityExcelDTO.class);// 設(shè)置為導(dǎo)出件格式為excelresponse.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String filePrefix="測試文件";try {// 這里URLEncoder.encode可以防止中文亂碼 當(dāng)然和easyexcel沒有關(guān)系String fileName = URLEncoder.encode(filePrefix, "UTF-8").replaceAll("\\+", "%20");//Content-disposition 的 attachment參數(shù)將文件作為附件下載response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");EasyExcel.write(response.getOutputStream(), TeleCityExcelDTO.class).sheet("模板").doWrite(exportList);} catch (Exception e) {log.error("下載文件失敗!", e);throw new BizException(ResponseCode.EXPORT_FILE_FAILURE);}log.info("下載數(shù)據(jù)大小為:{}", exportList.size());}5.Upload上傳文件
類似于前面的讀取excel文件,這里是通過前端選取文件,讀入到輸入流里面,然后通過監(jiān)聽器讀取指定格式。注意接收文件的類型為:MultipartFile
@RequestMapping("/test18") public Response testUpLoadExcel(MultipartFile file){log.info("文件名稱:{}",file.getName());//構(gòu)建上傳文件的數(shù)據(jù)格式GeneralListener<TeleCityExcelDTO> generalListener=new GeneralListener<>();try {EasyExcel.read(file.getInputStream(), TeleCityExcelDTO.class, generalListener).sheet().doRead();} catch (IOException e) {log.error("上傳文件失敗!", e);throw new BizException(ResponseCode.UP_LOAD_FILE_FAILURE);}return new Response("0","讀取數(shù)據(jù)成功!",generalListener.getList().size()); }前端樣式,注意讀取文件的類型為:multipart/form-data,前端
<form action="/test/test18", method="post", enctype="multipart/form-data"><input type="file" name="file"/><input type="submit"> </form>總結(jié)
以上是生活随笔為你收集整理的EasyExcel实现文件读取、导出、上传、下载操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring中策略模式实现方法
- 下一篇: Redis Lua脚本实现原子性操作