话不多说,直接进入整体
表格导出
在一般不管大的或者小的系统中,各家的产品都一样,闲的无聊的时候都喜欢让我们这些程序员导出一些数据出来供他观赏,非说这是必须需求,非做不可,那么我们就只能苦逼的哼哧哼哧的写bug喽。
之前使用POI导出excel需要自己先去创建excel文件,还要创建sheet,写表头,操作起来确实很麻烦,针对产品这种随心而欲的需求我们能不能快速的做完这样一个产品看起来挺简单的功能。
基于这样一个快速开发的场景,我们来看看easyexcel应该如何使用。
首先我们创建一个springboot(版本是 2.1.4.RELEASE)项目,在此就不过多的啰嗦,创建好之后,首先需要引入easyexcel的maven坐标。
com.alibabaeasyexcel1.1.2-beta5
导入好了之后,我们接下来需要创建一个导出的模板类,首先要集成BaseRowModel,set、get省略,@ExcelProperty注解中的value就是表头的信息,index是在第几列,没有加注解的不会导出。
publicclassOrderExcelBOextendsBaseRowModel{@ExcelProperty(value = {"订单ID"}, index =0)privateStringid;/**
* 订单描述
*/@ExcelProperty(value = {"订单描述"}, index =2)privateStringdescription;/**
* 订单对应产品id
*/@ExcelProperty(value = {"产品ID"}, index =2)privateIntegerproductId;/**
* 支付方式描述,如:apple pay
*/@ExcelProperty(value = {"支付方式"}, index =3)privateStringpayMethod;/**
* create_time
*/@ExcelProperty(value = {"时间"}, index =4)privateStringcreateTime;/**
* update_time
*/privateStringupdateTime;/**
* 产生订单的用户
*/@ExcelProperty(value = {"用户ID"}, index =5)privateIntegeruserId;/**
* 支付状态:0 未支付、1支付成功支付完成、-1支付失败
*/@ExcelProperty(value = {"支付状态"}, index =6)privateStringstatus;/**
* 订单来源描述,如:ios 安卓
*/@ExcelProperty(value = {"手机型号"}, index =7)privateStringplatform;/**
* 订单流水
*/@ExcelProperty(value = {"订单流水号"}, index =8)privateStringflowNum;/**
* 订单金额
*/@ExcelProperty(value = {"金额"}, index =9)privateBigDecimalprice;// @ExcelProperty(value = {"收据字段"}, index = 10)privateStringreceipt;@ExcelProperty(value = {"APP来源"}, index =10)privateStringsources;}
导出的模板定义好之后,接下来就是一些封装好的工具类的调用
查出我们需要导出的数据;
生成Excel文件名和sheet名称;
直接调用封装好的工具类导出文件即可;
我们来看下导出的效果
如果你的表头比较复杂,那么根据需求,你也可自行定义,例如如下这种复杂的表头,应该如何设置
首先要修改模板类,如果合并的单元格最大为2,那么所有的表格都需要设置为2,不合并的单元格用空字符串填充,需要合并的单元格将合并部分写上相同的名称,并且排列的序号要连续,不能分开。
我们来看下导出的效果,这样就可以满足我们平时开发需要的excel导出功能。简单易上手。
工具类:
importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importjava.util.ArrayList;importjava.util.List;publicclassExcelListenerextendsAnalysisEventListener{/**
* 自定义用于暂时存储data。
* 可以通过实例获取该值
*/privateList datas =newArrayList<>();/**
* 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
*/@Overridepublic void invoke(Objectobject,AnalysisContextcontext){//数据存储到list,供批量处理,或后续自己业务逻辑处理。datas.add(object);//根据业务自行 do somethingdoSomething();/*
如数据过大,可以进行定量分批处理
if(datas.size()<=100){
datas.add(object);
}else {
doSomething();
datas = new ArrayList<Object>();
}
*/}/**
* 根据业务自行实现该方法
*/privatevoid doSomething() { }@Overridepublic void doAfterAllAnalysed(AnalysisContextcontext) {/*
datas.clear();
解析结束销毁不用的资源
*/} publicList getDatas() {returndatas; } public void setDatas(List datas) {this.datas = datas; }}
importcom.alibaba.excel.ExcelReader;importcom.alibaba.excel.ExcelWriter;importcom.alibaba.excel.metadata.BaseRowModel;importcom.alibaba.excel.metadata.Font;importcom.alibaba.excel.metadata.Sheet;importcom.alibaba.excel.metadata.TableStyle;importcom.alibaba.excel.support.ExcelTypeEnum;importcom.mochu.exception.ExcelException;importorg.apache.poi.poifs.filesystem.FileMagic;importorg.apache.poi.ss.usermodel.IndexedColors;importorg.springframework.web.multipart.MultipartFile;importjavax.servlet.http.HttpServletResponse;importjava.io.BufferedInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.util.List;publicclassExcelUtil{/** * 读取 Excel(多个 sheet) * *@paramexcel 文件 *@paramrowModel 实体类映射,继承 BaseRowModel 类 *@returnExcel 数据 list */publicstaticList readExcel(MultipartFile excel, BaseRowModel rowModel) { ExcelListener excelListener =newExcelListener(); ExcelReader reader = getReader(excel, excelListener);if(reader ==null) {returnnull; }for(Sheet sheet : reader.getSheets()) {if(rowModel !=null) { sheet.setClazz(rowModel.getClass()); } reader.read(sheet); }returnexcelListener.getDatas(); }/** * 读取某个 sheet 的 Excel * *@paramexcel 文件 *@paramrowModel 实体类映射,继承 BaseRowModel 类 *@paramsheetNo sheet 的序号 从1开始 *@returnExcel 数据 list */publicstaticList readExcel(MultipartFile excel, BaseRowModel rowModel,intsheetNo) {returnreadExcel(excel, rowModel, sheetNo,1); }/** * 读取某个 sheet 的 Excel * *@paramexcel 文件 *@paramrowModel 实体类映射,继承 BaseRowModel 类 *@paramsheetNo sheet 的序号 从1开始 *@paramheadLineNum 表头行数,默认为1 *@returnExcel 数据 list */publicstaticList readExcel(MultipartFile excel, BaseRowModel rowModel,intsheetNo,intheadLineNum) { ExcelListener excelListener =newExcelListener(); ExcelReader reader = getReader(excel, excelListener);if(reader ==null) {returnnull; } reader.read(newSheet(sheetNo, headLineNum, rowModel.getClass()));returnexcelListener.getDatas(); }/** * 导出 Excel :一个 sheet,带表头 * *@paramresponse HttpServletResponse *@paramlist 数据 list,每个元素为一个 BaseRowModel *@paramfileName 导出的文件名 *@paramsheetName 导入文件的 sheet 名 *@paramobject 映射实体类,Excel 模型 */publicstaticvoidwriteExcel(HttpServletResponse response, List list, String fileName, String sheetName, BaseRowModel object){ ExcelWriter writer =newExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX); Sheet sheet =newSheet(1,0, object.getClass()); sheet.setSheetName(sheetName); TableStyle tableStyle =newTableStyle(); tableStyle.setTableContentBackGroundColor(IndexedColors.WHITE); Font font =newFont(); font.setFontHeightInPoints((short)9); tableStyle.setTableHeadFont(font); tableStyle.setTableContentFont(font); sheet.setTableStyle(tableStyle); writer.write(list, sheet); writer.finish(); }/** * 导出 Excel :多个 sheet,带表头 * *@paramresponse HttpServletResponse *@paramlist 数据 list,每个元素为一个 BaseRowModel *@paramfileName 导出的文件名 *@paramsheetName 导入文件的 sheet 名 *@paramobject 映射实体类,Excel 模型 */publicstaticExcelWriterFactorywriteExcelWithSheets(HttpServletResponse response, List list, String fileName, String sheetName, BaseRowModel object){ ExcelWriterFactory writer =newExcelWriterFactory(getOutputStream(fileName, response), ExcelTypeEnum.XLSX); Sheet sheet =newSheet(1,0, object.getClass()); sheet.setSheetName(sheetName); sheet.setTableStyle(getTableStyle()); writer.write(list, sheet);returnwriter; }/** * 导出融资还款情况表 * *@paramresponse *@paramlist *@paramfileName *@paramsheetName *@paramobject */publicstaticvoidwriteFinanceRepayment(HttpServletResponse response, List list, String fileName, String sheetName, BaseRowModel object){ ExcelWriter writer =newExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX); Sheet sheet =newSheet(1,0, object.getClass()); sheet.setSheetName(sheetName); sheet.setTableStyle(getTableStyle()); writer.write(list, sheet);for(inti =1; i <= list.size(); i +=4) { writer.merge(i, i +3,0,0); writer.merge(i, i +3,1,1); } writer.finish(); }/**
* 导出文件时为Writer生成OutputStream
*/privatestaticOutputStreamgetOutputStream(String fileName, HttpServletResponse response){//创建本地文件fileName = fileName +".xls";try{ fileName =newString(fileName.getBytes(),"ISO-8859-1"); response.addHeader("Content-Disposition","filename="+ fileName);returnresponse.getOutputStream(); }catch(Exception e) {thrownewExcelException("导出异常!"); } }/** * 返回 ExcelReader * *@paramexcel 需要解析的 Excel 文件 *@paramexcelListener new ExcelListener() */privatestaticExcelReadergetReader(MultipartFile excel, ExcelListener excelListener){ String filename = excel.getOriginalFilename();if(filename ==null|| (!filename.toLowerCase().endsWith(".xls") && !filename.toLowerCase().endsWith(".xlsx"))) {thrownewExcelException("文件格式错误!"); } InputStream inputStream;try{ inputStream =newBufferedInputStream(excel.getInputStream());returnnewExcelReader(inputStream,null, excelListener,false); }catch(IOException e) { e.printStackTrace(); }returnnull; }/** * 资金收支导出 Excel :一个 sheet,带表头 * *@paramresponse HttpServletResponse *@paramlist 数据 list,每个元素为一个 BaseRowModel *@paramfileName 导出的文件名 *@paramsheetName 导入文件的 sheet 名 *@paramobject 映射实体类,Excel 模型 */publicstaticvoidexportFundBudgetExcel(HttpServletResponse response, List list, String fileName, String sheetName, BaseRowModel object)throwsIOException{ ExcelWriter writer =newExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX); Sheet sheet =newSheet(1,0, object.getClass()); sheet.setSheetName(sheetName); sheet.setTableStyle(getTableStyle()); writer.write(list, sheet); writer.merge(2,3,0,0); writer.merge(4,13,0,0); writer.merge(14,14,0,1); writer.finish(); }/** * 读取Excel表格数据,封装成实体 * *@paraminputStream *@paramclazz *@paramsheetNo *@paramheadLineMun *@return*/publicstaticObjectreadExcel(InputStream inputStream, Class clazz, Integer sheetNo, Integer headLineMun){if(null== inputStream) {thrownewNullPointerException("the inputStream is null!"); } ExcelListener listener =newExcelListener(); ExcelReader reader =newExcelReader(inputStream, valueOf(inputStream),null, listener); reader.read(newSheet(sheetNo, headLineMun, clazz));returnlistener.getDatas(); }/**
* 根据输入流,判断为xls还是xlsx,该方法原本存在于easyexcel 1.1.0 的ExcelTypeEnum中。
*/publicstaticExcelTypeEnumvalueOf(InputStream inputStream){try{ FileMagic fileMagic = FileMagic.valueOf(inputStream);if(FileMagic.OLE2.equals(fileMagic)) {returnExcelTypeEnum.XLS; }if(FileMagic.OOXML.equals(fileMagic)) {returnExcelTypeEnum.XLSX; }thrownewExcelException("excelTypeEnum can not null"); }catch(IOException e) {thrownewRuntimeException(e); } }/** * 设置全局样式 * *@return*/privatestaticTableStylegetTableStyle(){ TableStyle tableStyle =newTableStyle(); tableStyle.setTableContentBackGroundColor(IndexedColors.WHITE); Font font =newFont(); font.setBold(true); font.setFontHeightInPoints((short)9); tableStyle.setTableHeadFont(font); Font fontContent =newFont(); fontContent.setFontHeightInPoints((short)9); tableStyle.setTableContentFont(fontContent);returntableStyle; }}
importcom.alibaba.excel.ExcelWriter;importcom.alibaba.excel.metadata.BaseRowModel;importcom.alibaba.excel.metadata.Sheet;importcom.alibaba.excel.support.ExcelTypeEnum;importjava.io.IOException;importjava.io.OutputStream;importjava.util.List;publicclassExcelWriterFactoryextendsExcelWriter{privateOutputStreamoutputStream;privateint sheetNo =1; publicExcelWriterFactory(OutputStreamoutputStream,ExcelTypeEnumtypeEnum) {super(outputStream, typeEnum);this.outputStream = outputStream; } publicExcelWriterFactorywrite(List list,StringsheetName,BaseRowModelobject){this.sheetNo++;try{Sheetsheet =newSheet(sheetNo,0,object.getClass()); sheet.setSheetName(sheetName);this.write(list, sheet); }catch(Exceptionex) { ex.printStackTrace();try{ outputStream.flush(); }catch(IOExceptione) { e.printStackTrace(); } }returnthis; }@Overridepublic void finish() {super.finish();try{ outputStream.flush(); }catch(IOExceptione) { e.printStackTrace(); } }}
表格导入
我们先看下Controller是如何写的
@PostMapping("/import/order")publicResultMsgimport_order(MultipartFile excel){ Object objList = ExcelUtil.readExcel(excel,newOrderExcelBO(),1,1);if(objList ==null) {returnResultMsg.fail(500,"导入的数据不能为空"); } List orderList = (List) objList;if(orderList ==null|| orderList.size() <=0) {returnResultMsg.fail(500,"导入的数据不能为空"); } orderList.forEach(System.out::println);returnResultMsg.success();}
我们主要再看下readExcel()方法是如何写的
/** * 读取某个 sheet 的 Excel * *@paramexcel 文件 *@paramrowModel 实体类映射,继承 BaseRowModel 类 *@paramsheetNo sheet 的序号 从1开始 *@paramheadLineNum 表头行数,默认为1 *@returnExcel 数据 list */publicstaticList readExcel(MultipartFile excel, BaseRowModel rowModel,intsheetNo,intheadLineNum) { ExcelListener excelListener =newExcelListener(); ExcelReader reader = getReader(excel, excelListener);if(reader ==null) {returnnull; } reader.read(newSheet(sheetNo, headLineNum, rowModel.getClass()));returnexcelListener.getDatas(); }
在底层的方法我就不贴出来了,我大家可在(SpringBoot整合easyexcel实现Excel的导出)文章里面找到需要的工具类,我们就直接看下运行结果。
导出结果
网友评论