一、Apach POI處理Excel的方式: 傳統Excel操作或者解析都是利用Apach POI進行操作,POI中處理Excel有以下幾種方式: 1、HSSFWorkbook: HSSFWorkbook用來處理.xls尾碼的Excel,即適用於Excel2003以前(包括2003)的版本。因為其最 ...
一、Apach POI處理Excel的方式:
傳統Excel操作或者解析都是利用Apach POI進行操作,POI中處理Excel有以下幾種方式:
1、HSSFWorkbook:
HSSFWorkbook用來處理.xls尾碼的Excel,即適用於Excel2003以前(包括2003)的版本。因為其最大隻能處理65535行的數據,所以現在已經很少使用了
2、XSSFWorkbook:
XSSFWorkbook是現在處理Excel比較常見的方式。其適用於.xlsx尾碼的Excel,即Excel2007後的版本。能夠最多處理104萬行數據。但是其在讀取/處理Excel時會一口氣將Excel內容寫入到記憶體,因此在處理的Excel文件較大時可能打爆記憶體,造成OOM異常(記憶體溢出)。
3、SXSSFWorkbook:
SXSSFWorkbook相當於是XSSFWorkbook的改良版本,在初始化SXSSFWorkbook實例時,需要填寫一個緩衝行數參數(預設100行),當讀入到記憶體中的數據超過該數值後,會像隊列一樣將最前面的數據保存到硬碟中,從而避免出現OOM。這麼一看該方式簡直完美啊,不過因為超過緩存行的數據都寫到硬碟中了,所以如果你想要獲取這塊的內容(比如複製這塊內容到另一個Excel中)就會發現取不到了,因為不在記憶體中,所以無法通過SXSSFWorkbook實例獲取該部分內容。
二、Apach POI框架的不足:
1、使用步驟繁瑣;
2、動態寫出Excel操作非常麻煩;
3、對於新手來說,很難在短時間內上手;
4、讀寫時需要占用較大的內容,當數據量大時容器發生OOM;
基於上述原因,阿裡開源出一款易上手,且比較節省記憶體的Excel操作框架:EasyExcel
三、Apach POI、EasyPoi與EasyExcel的區別:
1、POI 優點在於自由,但是迎來的就是複雜度,和大數據量時候性能的缺點
2、EasyPoi基於POI 的二次封裝,解決了大部分的常用場景,簡化了代碼,但是特別複雜表格處理還是不行,而且性能的話和poi差不多,簡單來說就是簡化了Poi的操作,少些點代碼,總體來說,easypoi和easyexcel都是基於apache poi進行二次開發的。
3、easypoi和easyexcel的不同點在於:
(1)、easypoi 在讀寫數據的時候,優先是先將數據寫入記憶體,優點是讀寫性能非常高,但是當數據量很大的時候,會出現oom,當然它也提供了 sax 模式的讀寫方式,需要調用特定的方法實現。
(2)、easyexcel 基於sax模式進行讀寫數據,不會出現oom情況,程式有過高併發場景的驗證,因此程式運行比較穩定,相對於 easypoi 來說,讀寫性能稍慢!
(3)、easypoi 與 easyexcel 還有一點區別在於,easypoi 對定製化的導出支持非常的豐富,如果當前的項目需求,併發量不大、數據量也不大,但是需要導出 excel 的文件樣式千差萬別,那麼我推薦你用 easypoi;反之,使用 easyexcel !
四、EasyExcel的使用:
4.1、操作流程:
建工程——》改POM——》寫YML——》業務類
4.1.1、添加依賴:
<!-- easyexcel 依賴 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.7</version> </dependency>
4.1.2、寫YML:
server: port: 8081 spring: application: name: demo #項目名 datasource: type: com.alibaba.druid.pool.DruidDataSource #當前數據源操作類型 driver-class-name: org.gjt.mm.mysql.Driver #mysql驅動包 url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 druid: test-while-idle: false #關閉空閑檢測 mybatis: mapperLocations: classpath:mapper/*.xml #resource目錄下建mapper包,存放xml文件
4.2、EasyExcel的導出操作(單個與批量導出):
4.2.1、通用導出工具類ExportUtil:
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.IndexedColors; import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.util.List; @Slf4j public class ExportUtil { /** * 導出 Excel * * @param response :響應 * @param data :導出的list集合 * @param fileName :Excel名(最好英文,無需尾碼) * @param sheetName :sheet頁名 * @param clazz :導出Excel實體類 * @throws Exception */ public static void writeExcel(HttpServletResponse response, List<? extends Object> data, String fileName, String sheetName, Class clazz) throws Exception { //表頭樣式 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); //設置表頭居中對齊 headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); //內容樣式 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); //設置內容靠左對齊 contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); EasyExcel.write(getOutputStream(fileName, response), clazz) .excelType(ExcelTypeEnum.XLSX)//讀取的文件類型 .sheet(sheetName)//讀取的sheet,可以是行號也可以是sheet名 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())//設置自動適應寬度 .registerWriteHandler(horizontalCellStyleStrategy)//設置樣式(或:registerWriteHandler(createStyleStrategy)) .doWrite(data); } /** * 格式處理 */ private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception { response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setCharacterEncoding("utf8"); response.addHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue()); return response.getOutputStream(); } /** * 自定義樣式 */ private static HorizontalCellStyleStrategy createStyleStrategy() { // 頭的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景設置為紅色 headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex()); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 10); headWriteCellStyle.setWriteFont(headWriteFont); // 內容的策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); contentWriteCellStyle.setBorderBottom(BorderStyle.THIN); //底邊框 contentWriteCellStyle.setBorderLeft(BorderStyle.THIN); //左邊框 contentWriteCellStyle.setBorderRight(BorderStyle.THIN); //右邊框 contentWriteCellStyle.setBorderTop(BorderStyle.THIN); //頂邊框 WriteFont contentWriteFont = new WriteFont(); // 字體大小 contentWriteFont.setFontHeightInPoints((short) 10); contentWriteCellStyle.setWriteFont(contentWriteFont); // 執行策略 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); return horizontalCellStyleStrategy; } }
4.2.2、業務流程:
1、請求體:
import lombok.Data; import java.util.List; @Data public class RequestVO { private String id; private List<String> ids; private Boolean way; }
2、控制類:
/** * 導出 * 1、單個導出 * 2、批量導出 * * http://localhost:8081/v1/export-data * { * "id":"xxx", * "ids":["xxx","xxx"], * "way":false * } * */ @PostMapping("/export-data") public void exportList( @RequestBody RequestVO requestVO, HttpServletResponse response) { //導出方式:單個/批量 if(true == requestVO.getWay()){ requestVO.setId(null); } operateService.exportData(requestVO,response); }
3、業務類:
@Autowired private OperateMapper operateMapper; /** * 導出操作 */ @Override public void exportData(RequestVO requestVO, HttpServletResponse response) { //mysql查詢 List<BasicDemo> list = operateMapper.selectAllDemo(requestVO); //轉換 List<ExportExcelVO> exportList = new ArrayList<>(); Optional.ofNullable(list).ifPresent(po -> { po.stream().forEach(p -> { ExportExcelVO exportExcelVO = ExportExcelVO.builder() .uuid(p.getUuid()) .name(p.getName()) .status(p.getStatus()) .startTime(p.getStartTime()) .build(); exportList.add(exportExcelVO); }); }); //導出 try { writeExcel(response, exportList, "result", "表名", ExportExcelVO.class); } catch (Exception e) { e.printStackTrace(); log.error("錯誤信息Exception:", e); } log.info("執行完畢,導出成功"); }
4、導出實體類:
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 com.alibaba.excel.annotation.write.style.ContentRowHeight; import com.alibaba.excel.annotation.write.style.HeadRowHeight; import lombok.Builder; import lombok.Data; import java.io.Serializable; @HeadRowHeight(value = 35) // 設置表頭行高 @ContentRowHeight(value = 25) // 設置內容行高 //@ColumnWidth(value = 50) // 設置列寬 @Data @Builder public class ExportExcelVO implements Serializable { @ExcelProperty(value = {"編號"},order = 1) private String uuid; @ExcelProperty(value = {"基本信息","詳情","名字"},order = 2) private String name; @ExcelProperty(value = {"基本信息","詳情","狀態"},order = 3) private String status; @ExcelProperty(value = {"基本信息","操作時間","時間"},order = 4) @DateTimeFormat("yyyy-MM-dd") //時間格式 private String startTime; }
5、SQL語句:
<select id="selectAllDemo" resultMap="BaseResultMap" parameterType="java.lang.String"> select <include refid="Base_Column_List"/> from page_demo where 1=1 <if test="id neq null and id != ''"> AND uuid = #{id} </if> <if test="ids != null and ids.size()!=0"> AND uuid in <foreach collection="ids" item="item" index="index" open="(" separator="," close=")"> #{item} </foreach> </if> </select>
6、導出樣本:
4.3、EasyExcel的導入操作(模板下載與數據導入):
4.3.1、通用導入工具類ImportUtil:
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import lombok.extern.slf4j.Slf4j; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @Slf4j public class ImportUtil { /** * 導入 Excel * * @param multipartFile excel文件 * @param clazz 數據類型的class對象 * @param <T> * @return */ public static <T> List<T> importExcel(MultipartFile multipartFile, Class<T> clazz) throws Exception { List<T> list = new ArrayList<>(); InputStream inputStream = multipartFile.getInputStream(); EasyExcel.read(inputStream, clazz, new AnalysisEventListener<T>() { //每解析一行就會調用一次,data數據表示解析出來一行的數據 @Override public void invoke(T data, AnalysisContext context) { list.add(data); } //當全部數據讀取完成後調用該方法 @Override public void doAfterAllAnalysed(AnalysisContext context) { log.info("解析完成"); } }).sheet().doRead(); return list; } }
4.3.2、業務流程:
1、控制類:
@Autowired private OperateService operateService; /** * 導入模板下載 * http://localhost:8081/v1/download * */ @GetMapping("/download") public void downloadTemplate(HttpServletResponse response) { operateService.importTemplate(response); } /** * 導入 * http://localhost:8081/v1/import-data */ @PostMapping(value="/import-data") public Response<List<ImportExcelVO>> importExcel(@RequestParam(value = "file") MultipartFile file) { List<ImportExcelVO> list = operateService.importData(file); return Response.success(list); }
2、業務類:
/** * 導入模板下載 */ @Override public void importTemplate(HttpServletResponse response) { List<ImportExcelVO> importExcelVO = new ArrayList<>(); try { //相當導出 Excel操作 writeExcel(response, importExcelVO, "importTemplate", "表名", ImportExcelVO.class); } catch (Exception e) { e.printStackTrace(); log.error("錯誤信息Exception:", e); } log.info("執行完畢,導出成功"); } /** * 導入操作 */ @Override public List<ImportExcelVO> importData(MultipartFile file) { List<ImportExcelVO> list = new ArrayList<>(); try { //獲取數據 list = importExcel(file, ImportExcelVO.class); } catch (Exception e) { e.printStackTrace(); } //數據操作: for (ImportExcelVO importExcelVO : list) { log.info("輸出:" + importExcelVO); } return list; }
4、導入實體類:
@HeadRowHeight(value = 35) // 設置表頭行高 @ContentRowHeight(value = 25) // 設置內容行高 //@ColumnWidth(value = 50) // 設置列寬 @Data public class ImportExcelVO implements Serializable { @ExcelProperty(value = {"編號"},order = 1) private String uuid; @ExcelProperty(value = {"基本信息","詳情","名字"},order = 2) private String name; @ExcelProperty(value = {"基本信息","詳情","狀態"},order = 3) private String status; @ExcelProperty(value = {"基本信息","操作時間","時間"},order = 4) @DateTimeFormat("yyyy-MM-dd") //時間格式 private String startTime; }
五、參考: