文章目录
- 前言
- 正文
- 一、项目依赖
- 二、封装表格实体和Sheet实体
- 2.1 表格实体
- 2.2 Sheet实体
- 三、核心实现
- 3.1 核心实现之导出为输出流
- 3.2 web导出
- 3.3 导出为字节数组
- 四、调试
- 4.1 构建调试用的实体类
- 4.2 控制器调用
- 4.3 测试结果
- 五、注册大数转换器,长度大于15时,转换为字符串
- 5.1 实现转换器
- 5.2 使用转换器
前言
工作中遇到一个需求,一次导出多个Excel 文件,并且每个excel中可能存在1到多个sheet页。
好在没有那种单元格合并的要求。
总体的思路是,设计两个实体,一个表示表格,一个表示sheet 数据。并且表格包含一个list 类型的sheet对象。
然后再使用ZipOutputStream
、ExcelWriterBuilder
、EasyExcel#writerSheet(...)
等类和方法去组装表格,最终进行压缩。
项目整体使用 java 8 和 阿里的easyexcel工具包。
正文
一、项目依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.0.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.11</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></exclusion></exclusions></dependency></dependencies>
二、封装表格实体和Sheet实体
2.1 表格实体
/*** excel数据*/
@Data
public static class ExcelData {// sheet的列表private final List<ExcelShellData<?>> shellDataList = new ArrayList<>();// 表格文件名private String filename;public void addShellData(ExcelShellData<?> excelShellData) {this.shellDataList.add(excelShellData);}
}
2.2 Sheet实体
/*** sheet数据*/
@Data
@AllArgsConstructor
public static class ExcelShellData<T> {// 数据private List<T> list;// sheet名private String sheetName;// 数据实体类型private Class<T> clazz;
}
三、核心实现
3.1 核心实现之导出为输出流
这一步主要组装表格数据,以及生成sheet。最终将数据放到输出流outputStream
中 。
private static void exportZipStream(List<ExcelData> excelDataList, OutputStream outputStream) {try {// 开始存入try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {try {for (ExcelData excelData : excelDataList) {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();com.alibaba.excel.ExcelWriter excelWriter = null;try {ExcelWriterBuilder builder = EasyExcel.write(byteArrayOutputStream).autoCloseStream(false)// 自动适配.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());excelWriter = builder.build();zipOut.putNextEntry(new ZipEntry(excelData.getFilename()));// 开始写入excelfor (ExcelShellData<?> shellData : excelData.getShellDataList()) {WriteSheet writeSheet = EasyExcel.writerSheet(shellData.getSheetName()).head(shellData.getClazz()).build();excelWriter.write(shellData.getList(), writeSheet);}} catch (Exception e) {throw new RuntimeException("导出Excel异常", e);} finally {if (excelWriter != null) {excelWriter.finish();}}byteArrayOutputStream.writeTo(zipOut);zipOut.closeEntry();}} catch (Exception e) {throw new RuntimeException("导出Excel异常", e);}}} catch (IOException e) {throw new RuntimeException("导出Excel异常", e);}}
3.2 web导出
可以调用本方法,直接在Controller中调用之后,当访问对应url,会直接下载到浏览器。
/*** 导出多个sheet到多个excel文件,并压缩到一个zip文件*/public static void exportZip(String zipFilename, List<ExcelData> excelDataList, HttpServletResponse response) {try {// 这里URLEncoder.encode可以防止中文乱码zipFilename = URLEncoder.encode(zipFilename, "utf-8");// 指定文件名response.setHeader("Content-disposition", "attachment;filename=" + zipFilename);response.setContentType("application/x-msdownload");response.setCharacterEncoding("utf-8");exportZipStream(excelDataList, response.getOutputStream());} catch (IOException e) {throw new RuntimeException("导出Excel异常", e);}}
3.3 导出为字节数组
当我们需要导出为字节数组时,可以调用本方法。之后随你怎么加工。
/*** 导出多个sheet到多个excel文件,并压缩到一个zip文件。最终得到一个字节数组。*/public static byte[] exportZip(List<ExcelData> excelDataList) {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();exportZipStream(excelDataList, byteArrayOutputStream);return byteArrayOutputStream.toByteArray();}
四、调试
4.1 构建调试用的实体类
指定简单的几个字段作为导出数据。
@Data@AllArgsConstructorpublic static class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;}
4.2 控制器调用
组装假数据,进行导出。
@Controller
@RequestMapping("/excel")
public class ExcelDemoController {@GetMapping("/exportTransportDetail")public String exportTransportDetail(HttpServletResponse response) throws IOException {// 压缩包文件名String fileName = "结算单运单明细-" + System.currentTimeMillis() + ".zip";List<ExcelData> excelDataList = new ArrayList<>();// 第一个ExcelExcelData excelData1 = new ExcelData();excelData1.setFilename("结算单运单明细-1-" + System.currentTimeMillis() + ".xlsx");List<DemoData> demoData1 = new ArrayList<>();demoData1.add(new DemoData("excel-sheet1", new Date(), 123112.321));demoData1.add(new DemoData("excel-sheet1", new Date(), 34.3));List<DemoData> demoData2 = new ArrayList<>();demoData2.add(new DemoData("excel-sheet2", new Date(), 123112.321));demoData2.add(new DemoData("excel-sheet2", new Date(), 34.3));ExcelShellData<DemoData> shellData1 = new ExcelShellData<>(demoData1, "sheet1", DemoData.class);ExcelShellData<DemoData> shellData2 = new ExcelShellData<>(demoData2, "sheet2", DemoData.class);excelData1.addShellData(shellData1);excelData1.addShellData(shellData2);// 第2个ExcelExcelData excelData2 = new ExcelData();excelData2.setFilename("结算单运单明细-2-" + System.currentTimeMillis() +".xlsx");List<DemoData> demoData21 = new ArrayList<>();demoData21.add(new DemoData("excel-sheet21", new Date(), 123112.321));demoData21.add(new DemoData("excel-sheet22", new Date(), 34.3));List<DemoData> demoData22 = new ArrayList<>();demoData22.add(new DemoData("excel-sheet21", new Date(), 123112.321));demoData22.add(new DemoData("excel-sheet22", new Date(), 34.3));ExcelShellData<DemoData> shellData21 = new ExcelShellData<>(demoData21, "sheet1", DemoData.class);ExcelShellData<DemoData> shellData22 = new ExcelShellData<>(demoData22, "sheet2", DemoData.class);excelData2.addShellData(shellData21);excelData2.addShellData(shellData22);excelDataList.add(excelData1);excelDataList.add(excelData2);// 写法1///// exportZip(fileName, excelDataList , response);// 写法2///byte[] bytes = exportZip(excelDataList);response.setHeader("Content-disposition", "attachment;filename=" + fileName);response.setContentType("application/x-msdownload");response.setCharacterEncoding("utf-8");response.getOutputStream().write(bytes);response.getOutputStream().flush();return "succ";}
}
4.3 测试结果
可以看到压缩包解压后的效果:
其中一个文件内容如下:
sheet1:
sheet2:
五、注册大数转换器,长度大于15时,转换为字符串
5.1 实现转换器
/*** Excel 数值长度大于maxLength的数值转换为字符串*/public static class ExcelBigNumberConvert implements Converter<Long> {private final int maxLength;public ExcelBigNumberConvert() {this(15);}public ExcelBigNumberConvert(Integer maxLength) {this.maxLength = maxLength;}@Overridepublic Class<Long> supportJavaTypeKey() {return Long.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic Long convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {Object data = cellData.getData();if (data == null) {return null;}String s = String.valueOf(data);if (s.matches("^\\d+$")) {return Long.parseLong(s);}return null;}@Overridepublic CellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {if (object != null) {String str = object.toString();if (str.length() > maxLength) {return new CellData<>(str);}}return null;}}
5.2 使用转换器
在构建建造器时,增加注册转换器即可。