一、前言1.1、写在前面

截止2021年1月13日10:39:48,所涉及的项目中关于Excel导出的处理方式已经达到了3种:

① Apache poi

② ezsyPoi

③ mime (这个是前端导出,有兴趣的可以去查阅相关资料)

1.2、现状

① 整体来说,无论是哪种方式的实现,都能满足我们的日常需要,也就是说系统日常使用是OK的。

二、遇到问题2.1、标准不一致

这3中方式被用到的不同的场景之下,但是因为没有一个通用的规则,看起来有些许杂乱

2.2、增加业务复杂度

无论是业务增加还是问题排查,都要很好地get这三种场景下的操作代码,增加业务成本

2.3、不利于持续开发

负责该模块的开发人员增加了学习成本和基本使用成本

2.4、兼容性问题

昨天京东那边反馈说打不开我们导出的excel文件(他们是使用deoffice2016,我们使用的是wps)。

三、改进计划

针对上面的问题做一一修复!

3.1、统一标准

统一使用easyPoi作为技术栈

3.2、多处理场景处理覆盖

读取:

① 根据文件路径读取

② 前端上传文件处理

导出:

① 简单数据列表导出

② 复杂场景下的模板导出

③ 导出到本地的路径

3.3、配置式开发

适用场景广泛,且不能有硬编码

四、处理结果(工具类集成)

整理出一份工具类如下:

import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.Excel.entity.Exportparams; import cn.afterturn.easypoi.excel.entity.TemplateExportParams; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcelFactory; import com.alibaba.excel.ExcelReader; import com.hd.modules.excel.listener.StringExcelListener; import org.apache.commons.IO.IOUtils; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import Javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.util.List; import java.util.Map; @Component public class XaExcelUtil { /** * 功能描述: 数据列表型Excel导出 --> 可通过前端请求进行下载 * Param: [list, title, sheetName, pojoClass, fileName, response] * @param list 导出的实体类 * @param title 表头名称 * @param sheetName sheet表名 * @param pojoClass 映射的实体类 * @param fileName * @param response * Return: void */ public static void exportExcelForSimpleData(List<?> list, String title, String sheetName,Class<?> pojoClass, String fileName, HttpServletResponse response) { Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(title, sheetName), pojoClass, list); if (workbook != null) { try { response.setCharacterEncoding("utf-8"); response.setHeader("content-Type", "application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=" URLEncoder.encode(fileName, "UTF-8") ); workbook.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } } } /** * 功能描述: 根据模板导出Excel文件 * Param: [response, templatePath, fileName, data] * @param response response * @param templatePath 模板文件的路径(可以是一个放置于statics路径下的xls文件) * @param fileName 导出文件名 * @param data 模板中数据 * Return: void */ public static void exportExcelForTemplate(HttpServletResponse response, String templatePath,String fileName, Map<String, Object> data){ TemplateExportParams params = new TemplateExportParams(convertTemplatePath(templatePath), true); Workbook workbook = ExcelExportUtil.exportExcel(params, data); try (OutputStream outputStream = response.getOutputStream()) { response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition",\ "attachment;filename=" URLEncoder.encode(fileName, "utf-8")); workbook.write(outputStream); outputStream.flush(); } catch (Exception e) { e.printStackTrace(); } } /** * 功能描述: 把静态路径下文件内容复制到ClassPath下 * Param: [path] * Return: java.lang.String */ private static String convertTemplatePath(String path) { ClassPathResource resource = new ClassPathResource(path); File tempFile = new File(path); // 文件存在时 不再写入 //if (tempFile.exists()) { // return path; //} File parentFile = tempFile.getParentFile(); // 判断父文件夹是否存在 if (!parentFile.exists()) { parentFile.mkdirs(); } try { FileOutputStream fileOutputStream = new FileOutputStream(tempFile); BufferedInputStream bufferedInputStream = new BufferedInputStream(resource.getInputStream()); IOUtils.copy(bufferedInputStream, fileOutputStream); } catch (Exception e) { e.printStackTrace(); } return path; } /** * 功能描述: 导出excel文件到本地路径 * Param: [list, model, localPath] * @param list 数据集合 * @param model 接收的实体模型 * @param localPath 导出到的本地路径(C:\Users\Administrator\Desktop\writeExcel.xlsx) * Return: void */ public static void exportExcelToLocal(List<?> list, Class<?> model, String localPath){ EasyExcel.write(localPath, model).sheet("sheet1").doWrite(list); } /** * 功能描述: 读取excel文件,可以是来自前端或本地 * Param: [fileupload] * Return: java.util.List<java.util.List<java.lang.String>> */ public List<List<String>> readExcel(MultipartFile fileupload) { File file1 = null; try { file1 = File.createTempFile("temp", null); fileupload.transferTo(file1); FileInputStream fileInputStream = new FileInputStream(file1); // 也可以是本地路径 //FileInputStream fileInputStream = null; //try { //fileInputStream = new FileInputStream("D:/111.xlsx"); //} catch (Exception e) { //e.printStackTrace(); //} StringExcelListener listener = new StringExcelListener(); ExcelReader excelReader = EasyExcelFactory.read(fileInputStream, null, listener).headRowNumber(0).build(); excelReader.read(); List<List<String>> datas = listener.getDatas(); excelReader.finish(); return datas; } catch (Exception e) { e.printStackTrace(); } return null; } }

Java

五、使用示例5.1、前端导出简单的数据列表

① 创建一个a标签

<a href="/exportExcel">export</a>

Java

② 后端的业务处理

PS: 测试需要,直接写业务在controller中了

实体类:

import cn.afterturn.easypoi.excel.annotation.Excel; import com.alibaba.excel.annotation.ExcelIgnore; import lombok.Data; @Data public class ExcelExportTestVo { @ExcelIgnore private String vcCustomerId; @Excel(name = "客户名称",height = 11, width = 15) private String vcName; @Excel(name = "客户手机号",height = 11, width = 15) private String vcPphone; @Excel(name = "客户电话",height = 11, width = 15) private String vcTel; @Excel(name = "业务员",height = 11, width = 15) private String vcSalesman; @Excel(name = "是否未提交",replace = {"是_0", "否_1","否_2","否_3","否_4","否_5"}, height = 11, width = 15) private Integer orderStatus; /** * @Description: 客户订单统计 */ @Excel(name = "客户订单总计",height = 11, width = 15) private Integer orderTotal; /** * @Description: 客户订单金额统计 */ @Excel(name = "客户订单总金额",height = 11, width = 15) private Double orderMoneyTotal; }

Java

// 业务处理 @RequestMapping("/exportExcel") public void exportExcel(HttpServletResponse response){ List<ExcelExportTestVo> excelExportTestVoList = new ArrayList<>(); for(int i=0;i<10;i ){ ExcelExportTestVo excelExportTestVo = new ExcelExportTestVo(); excelExportTestVo.setOrderMoneyTotal(Double.valueOf(i)); excelExportTestVo.setOrderStatus(i); excelExportTestVo.setOrderTotal(i); excelExportTestVo.setVcName("name" i); excelExportTestVo.setVcPphone("phone" i); excelExportTestVo.setVcSalesman("ss" i); excelExportTestVo.setVcTel("tel" i); excelExportTestVoList.add(excelExportTestVo); } XaExcelUtil.exportExcelForSimpleData(excelExportTestVoList,"客户订单统计表", "客户订单统计", ExcelExportTestVo.class,"客户订单统计表.xls",response); }

5.2、前端导出复杂模板文件

① 创建模板文件如下图:

excel数据处理全能手册(支持文件读取及前后端导出)(1)

② 前端请求如下:

PS: HttpClient相关可参考:https://guangmuhua.com/articleDetail/237

httpClient.download({ url: '/exportExcel', data: { } }).then(res => { });

Java

③ 后端处理代码

@RequestMapping("/exportExcel") public void exportExcel(HttpServletResponse response){ List<ExcelExportTestVo> excelExportTestVoList = new ArrayList<>(); for(int i=1;i<5;i ){ ExcelExportTestVo excelExportTestVo = new ExcelExportTestVo(); excelExportTestVo.setVcCustomerId(String.valueOf(i)); excelExportTestVo.setOrderMoneyTotal(Double.valueOf(i)); excelExportTestVo.setOrderStatus(i); excelExportTestVo.setOrderTotal(i); excelExportTestVo.setVcName("name" i); excelExportTestVo.setVcPphone("phone" i); excelExportTestVo.setVcSalesman("ss" i); excelExportTestVo.setVcTel("tel" i); excelExportTestVoList.add(excelExportTestVo); } Map<String, Object> data = new HashMap<>(); data.put("bankName", "bankName test"); data.put("owner", "owner test"); data.put("account", "account test"); data.put("address", "addtess test"); data.put("swiftCode", "swiftCode test"); data.put("orderList", excelExportTestVoList); data.put("messers", "message"); data.put("settlement", "settlement test"); data.put("attn", "attn test"); data.put("fax", "fax test"); data.put("tel", "tel test"); data.put("duration", "duration test"); XaExcelUtil.exportExcelForTemplate(response, "statics/excel/excelExport.xls", "tmplateExport.xls", data); }

④ 整体效果如下

PS: 达到效果,然美观不足。兼容Office2016和wps

5.3、导出文件到本地

① 前端请求

<a href="/exportExcel">export</a>

Java

② 后端处理如下:

@RequestMapping("/exportExcel") public void exportExcel(HttpServletResponse response){ List<ExcelModel> modelList = new ArrayList<>(); for(int i=0;i<11;i ){ ExcelModel excelModelTmp = new ExcelModel(); excelModelTmp.setOrderNo("YD" i); excelModelTmp.setWarehouseDate(new Date()); modelList.add(excelModelTmp); } XaExcelUtil.exportExcelToLocal(modelList, ExcelModel.class, "C:\\Users\\Administrator\\Desktop\\writeExcel.xlsx"); }

③ 效果如下

excel数据处理全能手册(支持文件读取及前后端导出)(2)

5.4、读取文件

直接调readExcel方法即可,会返回一个List<List<String>>

格式的数据。以上述的excel文件为例:

[

[订单号, null, 包裹入库日期],[YD0, null, 2021-01-13 14:46:41], [YD1, null, 2021-01-13 14:46:41], [YD2, null, 2021-01-13 14:46:41], [YD3, null, 2021-01-13 14:46:41],[YD4, null, 2021-01-13 14:46:41], [YD5, null, 2021-01-13 14:46:41],[YD6, null, 2021-01-13 14:46:41], [YD7, null, 2021-01-13 14:46:41], [YD8, null, 2021-01-13 14:46:41],[YD9, null, 2021-01-13 14:46:41], [YD10, null, 2021-01-13 14:46:41]]

六、后记

内容整理与历史项目,参考网络相关文档。如有错误敬请斧正!

,