【EasyExcel实践】导出多个sheet到多个excel文件,并压缩到一个zip文件

文章目录

前言

工作中遇到一个需求,一次导出多个Excel 文件,并且每个excel中可能存在1到多个sheet页。

好在没有那种单元格合并的要求。

总体的思路是,设计两个实体,一个表示表格,一个表示sheet 数据。并且表格包含一个list 类型的sheet对象。

然后再使用ZipOutputStreamExcelWriterBuilderEasyExcel#writerSheet(...) 等类和方法去组装表格,最终进行压缩。

项目整体使用 java 8 和 阿里的easyexcel工具包。

正文

一、项目依赖

xml 复制代码
<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 表格实体

java 复制代码
/**
 * 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实体

java 复制代码
/**
 * sheet数据
 */
@Data
@AllArgsConstructor
public static class ExcelShellData<T> {
	// 数据
    private List<T> list;
    // sheet名
    private String sheetName;
    // 数据实体类型
    private Class<T> clazz;
}

三、核心实现

3.1 核心实现之导出为输出流

这一步主要组装表格数据,以及生成sheet。最终将数据放到输出流outputStream中 。

java 复制代码
    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()));
                            // 开始写入excel
                            for (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,会直接下载到浏览器。

java 复制代码
    /**
     * 导出多个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 导出为字节数组

当我们需要导出为字节数组时,可以调用本方法。之后随你怎么加工。

java 复制代码
    /**
     * 导出多个sheet到多个excel文件,并压缩到一个zip文件。最终得到一个字节数组。
     */
    public static byte[] exportZip(List<ExcelData> excelDataList) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        exportZipStream(excelDataList, byteArrayOutputStream);
        return byteArrayOutputStream.toByteArray();
    }

四、调试

4.1 构建调试用的实体类

指定简单的几个字段作为导出数据。

java 复制代码
    @Data
    @AllArgsConstructor
    public static class DemoData {
        @ExcelProperty("字符串标题")
        private String string;
        @ExcelProperty("日期标题")
        private Date date;
        @ExcelProperty("数字标题")
        private Double doubleData;
    }

4.2 控制器调用

组装假数据,进行导出。

java 复制代码
@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<>();
        // 第一个Excel
        ExcelData 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个Excel
        ExcelData 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 实现转换器

java 复制代码
    /**
     * Excel 数值长度大于maxLength的数值转换为字符串
     */
    public static class ExcelBigNumberConvert implements Converter<Long> {

        private final int maxLength;

        public ExcelBigNumberConvert() {
            this(15);
        }

        public ExcelBigNumberConvert(Integer maxLength) {
           this.maxLength = maxLength;
        }

        @Override
        public Class<Long> supportJavaTypeKey() {
            return Long.class;
        }

        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }

        @Override
        public 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;
        }

        @Override
        public 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 使用转换器

在构建建造器时,增加注册转换器即可。

相关推荐
不吃鱼的羊2 小时前
Excel生成DBC脚本源文件
服务器·网络·excel
chenchihwen2 小时前
数据分析时的json to excel 转换的好用小工具
数据分析·json·excel
lxxxxl4 小时前
C#调用OpenXml,读取excel行数据,遇到空单元跳过现象处理
excel
m0_748246355 小时前
前端通过new Blob下载文档流(下载zip或excel)
前端·excel
不吃鱼不吃鱼15 小时前
Excel加载项入门:原理、安装卸载流程与常见问题
excel·wps
深耕AI15 小时前
在Excel中绘制ActiveX控件:解决文本编辑框定位问题
java·前端·excel
五VV15 小时前
Note2024122001_Excel按成绩排名
excel
Eiceblue16 小时前
Python拆分Excel - 将工作簿或工作表拆分为多个文件
开发语言·python·excel
Excel_easy16 小时前
批量生成二维码,助力数字化管理-Excel易用宝
excel·wps
见未见过的风景20 小时前
将三个list往一个excel表的三个sheet中写入,能用多线程提高写入速度
数据结构·list·excel