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

文章目录

  • 前言
  • 正文
    • 一、项目依赖
    • 二、封装表格实体和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对象。

然后再使用ZipOutputStreamExcelWriterBuilderEasyExcel#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()));
                            // 开始写入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,会直接下载到浏览器。

    /**
     * 导出多个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
    @AllArgsConstructor
    public 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<>();
        // 第一个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 实现转换器


    /**
     * 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 使用转换器

在构建建造器时,增加注册转换器即可。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/199658.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

XML Schema中的attributeFormDefault

XML Schema中的attributeFormDefault属性&#xff0c;用以指定元素的属性默认是否必须带有命名空间前缀。 attributeFormDefault属性可以取值qualified或unqualified&#xff0c;默认值是unqualified。 当取值为qualified时&#xff0c;表示属性必须用命名空间作为前缀&#x…

线性可分SVM摘记

线性可分SVM摘记 0. 线性可分1. 训练样本到分类面的距离2. 函数间隔和几何间隔、(硬)间隔最大化3. 支持向量 \qquad 线性可分的支持向量机是一种二分类模型&#xff0c;支持向量机通过核技巧可以成为非线性分类器。本文主要分析了线性可分的支持向量机模型&#xff0c;主要取自…

命令模式 rust和java实现

文章目录 命令模式介绍javarustrust仓库 命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种数据驱动的设计模式。请求以命令的形式包裹在对象中&#xff0c;并传给调用对象。调用对象寻找可以处理该命令的合适的对象&#xff0c;并把该命令传给相应的对象&…

梦极光(ez_re?)

ez_re 先查壳看看&#xff0c;没有壳 32位 我先说说这道题 打开分析找到主函数 在这里就是flag了&#xff0c;用十六进制转ascii码 我们先运行这个程序看看 我想说说我的想法 首先没看出来这里是十六进制转ascii码其次41D538数组用来干啥来的&#xff1f;题目里面给出的请…

untiy 配置iis服务器来打开webgl

最简单的方法是不需要配置服务器&#xff0c;打包的时候直接build and run&#xff0c;但是有时候如果我们需要调整js的内容&#xff0c;会很不方便&#xff0c;所以配置一个iis服务器还是很有必要的 首先要开启iis服务 控制面板&#xff0c;查看方式选类型&#xff0c;点击程…

android viewpager 禁止滑动

android viewpager 禁止滑动 前言一、viewpager 禁止滑动是什么&#xff0c;有现成方法吗&#xff1f;二、使用setOnTouchListener三、使用自定义viewpager总结 前言 本文介绍了本人有一个相关的需求需要实现这一功能&#xff0c;在过程中发现自己之前没做过&#xff0c;然后记…

用C++和python混合编写数据采集程序?

之前看过一篇文章&#xff0c;主要阐述的就是多种语言混合编写爬虫程序&#xff0c;结合各种语言自身优势写一个爬虫代码是否行得通&#xff1f;觉得挺有意思的&#xff0c;带着这样的问题&#xff0c;我尝试着利用我毕生所学写了一段C和python混合爬虫程序&#xff0c;目前运行…

基于Spring Boot的疫苗接种系统-计算机毕设 附源码 32315

基于Spring Boot的疫苗接种系统 摘 要 预防预接种工作实行网络信息化管理&#xff0c;是我国免疫规划工作发展的需要。接种信息实行网络信息化不仅是预防接种工作步入了一个新的台阶&#xff0c;更重要的是解决了多年疫苗接种过程种&#xff0c;免疫接种剂次不清&#xff0c;难…

2023-简单点-yolox-pytorch代码解析(一)-nets/darknet.py

yolox-pytorch: nets/darknet.py yolox网络结构yolox-pytorch目录今天解析注释net/darknet.pyFocusBaseConvDWConvSPPBottleneckDarknet未完待续。。。 yolox网络结构 yolox-pytorch目录 今天解析注释net/darknet.py #!/usr/bin/env python3 # 指定使用python3来执行此脚本 …

电子签名软件,在教育行业中如何应用?

电子签名软件简化签署流程&#xff0c;降低签署门槛&#xff0c;让更多人便捷地参与到签署中来。 微签作为国内电子签名软件的拓荒者之一&#xff0c;拥有19年的研发应用经验&#xff0c;提供专业的企业电子签名服务。微签的电子签名软件广泛应用于审批场景&#xff0c;实现高…

win系列:电脑设置关闭屏幕和休眠时间不起作用解决方案

电脑设置关闭屏幕和休眠时间不起作用解决方案 一. 笔记本电脑30s自动锁屏&#xff0c;怎么设置都没用?方法一&#xff1a;使用快捷键方法二&#xff1a;开始菜单设置如果需要对锁屏进行背景等的设置&#xff0c;建议你采用这个方法来进行。方法三&#xff1a;控制面板设置怎么…

从0开始学习JavaScript--JavaScript 箭头函数

JavaScript的现代语法&#xff0c;箭头函数&#xff08;Arrow Functions&#xff09;是一个不可忽视的重要部分。它们不仅提供了更简洁的语法&#xff0c;还改变了函数的作用域规则。在这篇文章中&#xff0c;将深入研究JavaScript箭头函数的概念、语法、用法以及它们与传统函数…

使用Git客户端向gitee免密推送项目代码(保姆级流程哦)

1.进入Git官网手动下载git的客户端可执行程序 一路next即可 2.找到安装路径下的3.进入git-bash 根据如下的代码一次执行只需要修改对应的username和自己再gitee中绑定的邮箱 4.分发私钥到邮箱 产生私钥的时候回车三次即可&#xff1b;查看私钥如下图及正常&#xff1b; 5.进…

C++设计模式——工厂模式 :简单工厂、工厂方法、抽象工厂

工厂模式可以分为三种&#xff0c;简单工厂模式&#xff0c;工厂方法模式和抽象工厂模式。 那么&#xff0c;这三种工厂模式长啥样&#xff0c;又为啥会衍生出这三种模式来呢&#xff1f;本篇和大家一起来学习总结一下。 一、简单工厂模式 简单工厂SimpleFactory 负责创建所有…

Zabbix 6 详细安装部署教程

目录 一、安装 MySQL 数据库 二、安装 zabbix 监控平台 三、编辑配置文件 四、启动服务 五、zabbix-web 安装 zabbix web 出图展示乱码问题解决方案 zabbix 的安装部署非常简单&#xff0c;官方提供了四种安装途径&#xff0c;分别是二进制 rpm 包安装方式、源码安装方…

使用 DMA 在 FPGA 中的 HDL 和嵌入式 C 之间传输数据

使用 DMA 在 FPGA 中的 HDL 和嵌入式 C 之间传输数据 该项目介绍了如何在 PL 中的 HDL 与 FPGA 中的处理器上运行的嵌入式 C 之间传输数据的基本结构。 介绍 鉴于机器学习和人工智能等应用的 FPGA 设计中硬件加速的兴起&#xff0c;现在是剥开几层“云雾”并讨论 HDL 之间来回传…

微信小程序仿网易严选(附精选源码32套,涵盖商城团购等)

商城主要实现的功能 首页、专题、分类、购物车、我的小程序授权登陆获取用户信息首页包含品牌制造页、品牌制造详情页面、新品首发页面、人气推荐页面、各分类列表商品详情页面&#xff0c;包含常见问题、大家都在看商品列表、加入购物车、收藏商品、立即购买、下订单、选择收…

在表格中显示字典的内容(根据后端返回的数据)vue3

进入页面&#xff0c;调接口&#xff0c;后端返回数据&#xff0c;indexType为0或者1&#xff0c;要用这个数据显示字典的内容 用插槽拿到数据 写一个函数&#xff0c;在模板中使用 const { proxy } getCurrentInstance(); // 字典-指标类型 const { index_type } proxy.u…

6.保留两位小数【2023.11.28】

1.问题描述 题中将给出一个具有许多小数位的浮点数&#xff0c;请将这个数字保存至小数点后两位&#xff0c;并输出。 2.解决思路 输入一个浮点数。 程序将浮点数保留两位小数并输出。 例如&#xff1a; formatted_float "{:.2f}".format(input_float)3.代码实…

echarts图表滚动条带动页面窗口滚动条的问题

网上搜了很多方法不管用&#xff0c;后来发现每次滚动echarts或者左右滑动echarts下方都会报错&#xff0c;报错提示如下&#xff0c;看看你们的图表是否这样报错&#xff1a; 报错信息如下&#xff1a;Unable to preventDefault inside passive event listener invocation 原…