• 【Java基础】Java导Excel攻略


    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
    img

    • 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老
    • 导航
      • 檀越剑指大厂系列:全面总结 java 核心技术点,如集合,jvm,并发编程 redis,kafka,Spring,微服务,Netty 等
      • 常用开发工具系列:罗列常用的开发工具,如 IDEA,Mac,Alfred,electerm,Git,typora,apifox 等
      • 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
      • 懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
      • 数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂

    非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

    一.简单介绍

    1.需求背景

    项目开发过程中,很多时候需要用到导出功能,将表格中的数据通过导出到 excel 的方式给到业务方核对数据,在模版不固定的情况下,如何快速的导出数据到 excel 显得尤为关键,本文将介绍导出 excel 的方式。

    2.常见情形

    1. 报表生成: 在业务应用中,经常需要生成各种形式的报表,以便对数据进行汇总、分析和可视化展示。将数据导出到 Excel 文件可以让用户方便地使用 Excel 等工具进行进一步的数据处理和分析。
    2. 数据备份: 导出数据到 Excel 文件是一种常见的备份手段。用户可以定期将系统中的关键数据导出到 Excel,以便在需要时进行恢复或迁移。
    3. 数据交换: 在与其他系统或应用程序进行数据交换时,导出数据到 Excel 是一种通用的方式。Excel 文件格式是广泛支持的,易于在不同系统之间进行数据传递。
    4. 用户下载: 提供给用户下载其个人或业务数据的功能。这对于在线服务、电子商务平台等应用程序是很常见的需求,用户可以将其数据保存到本地以备查阅。
    5. 批量操作: 在某些情况下,用户可能需要对大量数据进行批量操作,例如批量更新、删除或进行其他处理。将数据导出到 Excel,用户可以在本地应用程序中更轻松地执行这些操作。
    6. 报价单、发票等业务文档: 在销售和财务领域,导出 Excel 可以用于生成报价单、发票和其他业务文档,这些文档通常需要以表格形式呈现。
    7. 数据分享: 有时,用户可能希望分享特定数据的快照或分析结果。将数据导出到 Excel 文件可以方便地与其他人共享数据。

    二.基础使用

    1.Apache POI

    在 Java 中使用 POI 库(Apache POI)可以方便地操作 Excel 文件,包括导出数据和设置单元格的样式,其中包括背景颜色。下面是一个简单的例子,演示如何在 Java 中使用 POI 库导出带有背景颜色的 Excel 文件。

    2.pom 依赖

    首先,确保你的项目中包含了 Apache POI 库的依赖。如果使用 Maven,可以在 pom.xml 文件中添加以下依赖:

    <dependencies>
        
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poiartifactId>
            <version>5.0.0version>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.具体实现

    import org.apache.poi.ss.usermodel.*;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class ExcelExporter {
    
        public static void main(String[] args) {
            try {
                // 创建工作簿
                Workbook workbook = new XSSFWorkbook();
                // 创建工作表
                Sheet sheet = workbook.createSheet("Sheet1");
    
                // 创建样式
                CellStyle style = workbook.createCellStyle();
                style.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
    
                // 创建行和单元格,并设置背景颜色
                Row row = sheet.createRow(0);
                Cell cell = row.createCell(0);
                cell.setCellValue("内容");
                cell.setCellStyle(style);
    
                // 导出文件
                try (FileOutputStream fileOut = new FileOutputStream("workbook.xlsx")) {
                    workbook.write(fileOut);
                }
    
                // 关闭工作簿
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    三.EasyExcel

    1. EasyExcel

    上述使用的原生的 Apache POI 通用性不够强,在某些方面使用起来还是不够方便,接下来将介绍阿里的 EasyExcel 的使用,在 POI 的基础上进行了封装,方便开发者直接使用。

    EasyExcel 是阿里巴巴开源的一款基于 Java 的简单、高效的 Excel 文件读写工具。它可以帮助开发者更方便地进行 Excel 文件的读写操作,支持读取大数据量的 Excel 文件并且性能较好。

    2.相关特性

    1. 简单易用: EasyExcel 提供了简单的 API,易于上手,使用起来相对轻松。
    2. 高性能: EasyExcel 使用了基于注解的对象模型,采用零反射、零异常的设计,因此性能较好。在处理大量数据时,相比于一些其他库,EasyExcel 通常表现更出色。
    3. 支持读写 Excel: 提供了读写 Excel 文件的功能,可以实现从 Excel 文件中读取数据,也可以将数据写入 Excel 文件。
    4. 支持多种数据模型: 可以支持 Java 普通对象、Map、List 等多种数据模型,便于适应不同的数据结构。
    5. 支持复杂报表: 可以实现复杂报表的导入导出,包括多表头、合并单元格等。
    6. 支持自定义样式: 可以自定义 Excel 单元格样式,包括字体、颜色、边框等。
    7. 支持多种 Excel 格式: 可以读写多种 Excel 格式,包括 xls 和 xlsx。

    3.简单示例

    读取 Excel:

    // 读取 Excel 文件
    String fileName = "example.xlsx";
    EasyExcel.read(fileName, UserData.class, new UserDataListener()).sheet().doRead();
    
    • 1
    • 2
    • 3

    写入 Excel:

    // 写入 Excel 文件
    String fileName = "example.xlsx";
    List<UserData> data = initData(); // 初始化数据
    EasyExcel.write(fileName, UserData.class).sheet("Sheet1").doWrite(data);
    
    • 1
    • 2
    • 3
    • 4

    监听器示例:

    public class UserDataListener extends AnalysisEventListener<UserData> {
    
        // 处理每一行的数据
        @Override
        public void invoke(UserData data, AnalysisContext context) {
            System.out.println("Read data: " + data);
        }
    
        // 所有数据解析完成后调用
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            System.out.println("All data parsed successfully.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这只是一个简单的示例,实际使用中可以根据需求进行更灵活和复杂的配置。EasyExcel 提供了更多的 API 和功能,以满足不同场景下的需求。可以通过 EasyExcel 的官方文档和示例代码深入了解其更多功能和用法:EasyExcel GitHub 仓库

    四.进阶使用

    1. EasyExcelUtil

    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.EasyExcelFactory;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.alibaba.excel.write.handler.WriteHandler;
    import org.apache.poi.ss.formula.functions.T;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URLEncoder;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    
    public class EasyExcelUtil {
    
        /**
         * 同步无模型读(默认读取sheet0,从第2行开始读)
         *
         * @param filePath excel文件的绝对路径
         */
        public static List<Map<Integer, String>> syncRead(String filePath) {
            return EasyExcelFactory.read(filePath).sheet().doReadSync();
        }
    
        /**
         * 同步无模型读(自定义读取sheetX,从第2行开始读)
         *
         * @param filePath excel文件的绝对路径
         * @param sheetNo  sheet页号,从0开始
         */
        public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo) {
            return EasyExcelFactory.read(filePath).sheet(sheetNo).doReadSync();
        }
    
        /**
         * 同步无模型读(指定sheet和表头占的行数)
         *
         * @param filePath
         * @param sheetNo    sheet页号,从0开始
         * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo, Integer headRowNum) {
            return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
        }
    
        /**
         * 同步无模型读(指定sheet和表头占的行数)
         *
         * @param inputStream
         * @param sheetNo     sheet页号,从0开始
         * @param headRowNum  表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum) {
            return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
        }
    
        /**
         * 同步无模型读(指定sheet和表头占的行数)
         *
         * @param file
         * @param sheetNo    sheet页号,从0开始
         * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static List<Map<Integer, String>> syncRead(File file, Integer sheetNo, Integer headRowNum) {
            return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
        }
    //====================================================无JAVA模型读取excel数据===============================================================
    
    //====================================================将excel数据同步到JAVA模型属性里===============================================================
    
        /**
         * 同步按模型读(默认读取sheet0,从第2行开始读)
         *
         * @param filePath
         * @param clazz    模型的类类型(excel数据会按该类型转换成对象)
         */
        public static List<T> syncReadModel(String filePath, Class clazz) {
            return EasyExcelFactory.read(filePath).sheet().head(clazz).doReadSync();
        }
    
        /**
         * 同步按模型读(默认表头占一行,从第2行开始读)
         *
         * @param filePath
         * @param clazz    模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo  sheet页号,从0开始
         */
        public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo) {
            return EasyExcelFactory.read(filePath).sheet(sheetNo).head(clazz).doReadSync();
        }
    
        /**
         * 同步按模型读(指定sheet和表头占的行数)
         *
         * @param inputStream
         * @param clazz       模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo     sheet页号,从0开始
         * @param headRowNum  表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static List<T> syncReadModel(InputStream inputStream, Class clazz, Integer sheetNo, Integer headRowNum) {
            return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
        }
    
        /**
         * 同步按模型读(指定sheet和表头占的行数)
         *
         * @param file
         * @param clazz      模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo    sheet页号,从0开始
         * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static List<T> syncReadModel(File file, Class clazz, Integer sheetNo, Integer headRowNum) {
            return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
        }
    
        /**
         * 同步按模型读(指定sheet和表头占的行数)
         *
         * @param filePath
         * @param clazz      模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo    sheet页号,从0开始
         * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo, Integer headRowNum) {
            return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
        }
    
        /**
         * 异步无模型读(默认读取sheet0,从第2行开始读)
         *
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param filePath      表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener) {
            EasyExcelFactory.read(filePath, excelListener).sheet().doRead();
        }
    
        /**
         * 异步无模型读(默认表头占一行,从第2行开始读)
         *
         * @param filePath      表头占的行数,从0开始(如果要连表头一起读出来则传0)
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param sheetNo       sheet页号,从0开始
         */
        public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo) {
            EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).doRead();
        }
    
        /**
         * 异步无模型读(指定sheet和表头占的行数)
         *
         * @param inputStream
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param sheetNo       sheet页号,从0开始
         * @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static void asyncRead(InputStream inputStream, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
            EasyExcelFactory.read(inputStream, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    
        /**
         * 异步无模型读(指定sheet和表头占的行数)
         *
         * @param file
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param sheetNo       sheet页号,从0开始
         * @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static void asyncRead(File file, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
            EasyExcelFactory.read(file, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    
        /**
         * 异步无模型读(指定sheet和表头占的行数)
         *
         * @param filePath
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param sheetNo       sheet页号,从0开始
         * @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)
         * @return
         */
        public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
            EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    
        /**
         * 异步按模型读取(默认读取sheet0,从第2行开始读)
         *
         * @param filePath
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param clazz         模型的类类型(excel数据会按该类型转换成对象)
         */
        public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz) {
            EasyExcelFactory.read(filePath, clazz, excelListener).sheet().doRead();
        }
    
        /**
         * 异步按模型读取(默认表头占一行,从第2行开始读)
         *
         * @param filePath
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param clazz         模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo       sheet页号,从0开始
         */
        public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo) {
            EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).doRead();
        }
    
        /**
         * 异步按模型读取
         *
         * @param inputStream
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param clazz         模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo       sheet页号,从0开始
         * @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static void asyncReadModel(InputStream inputStream, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
            EasyExcelFactory.read(inputStream, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    
        /**
         * 异步按模型读取
         *
         * @param file
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param clazz         模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo       sheet页号,从0开始
         * @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static void asyncReadModel(File file, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
            EasyExcelFactory.read(file, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    
        /**
         * 异步按模型读取
         *
         * @param filePath
         * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
         * @param clazz         模型的类类型(excel数据会按该类型转换成对象)
         * @param sheetNo       sheet页号,从0开始
         * @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)
         */
        public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
            EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
        }
    
        /**
         * 无模板写文件
         *
         * @param filePath
         * @param head     表头数据
         * @param data     表内容数据
         */
        public static void write(String filePath, List<List<String>> head, List<List<Object>> data) {
            EasyExcel.write(filePath).head(head).sheet().doWrite(data);
        }
    
        /**
         * 无模板写文件
         *
         * @param filePath
         * @param head      表头数据
         * @param data      表内容数据
         * @param sheetNo   sheet页号,从0开始
         * @param sheetName sheet名称
         */
        public static void write(String filePath, List<List<String>> head, List<List<Object>> data, Integer sheetNo, String sheetName) {
            EasyExcel.write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data);
        }
    
        /**
         * 根据excel模板文件写入文件
         *
         * @param filePath
         * @param templateFileName
         * @param headClazz
         * @param data
         */
        public static void writeTemplate(String filePath, String templateFileName, Class headClazz, List data) {
            EasyExcel.write(filePath, headClazz).withTemplate(templateFileName).sheet().doWrite(data);
        }
    
        /**
         * 根据excel模板文件写入文件
         *
         * @param filePath
         * @param templateFileName
         * @param data
         */
        public static void writeTemplate(String filePath, String templateFileName, List data) {
            EasyExcel.write(filePath).withTemplate(templateFileName).sheet().doWrite(data);
        }
    
        /**
         * 按模板写文件
         *
         * @param filePath
         * @param headClazz 表头模板
         * @param data      数据
         */
        public static void write(String filePath, Class headClazz, List data) {
            EasyExcel.write(filePath, headClazz).sheet().doWrite(data);
        }
    
        /**
         * 按模板写文件
         *
         * @param filePath
         * @param headClazz 表头模板
         * @param data      数据
         * @param sheetNo   sheet页号,从0开始
         * @param sheetName sheet名称
         */
        public static void write(String filePath, Class headClazz, List data, Integer sheetNo, String sheetName) {
            EasyExcel.write(filePath, headClazz).sheet(sheetNo, sheetName).doWrite(data);
        }
    
        /**
         * 按模板写文件
         *
         * @param filePath
         * @param headClazz    表头模板
         * @param data         数据
         * @param writeHandler 自定义的处理器,比如设置table样式,设置超链接、单元格下拉框等等功能都可以通过这个实现(需要注册多个则自己通过链式去调用)
         * @param sheetNo      sheet页号,从0开始
         * @param sheetName    sheet名称
         */
        public static void write(String filePath, Class headClazz, List data, WriteHandler writeHandler, Integer sheetNo, String sheetName) {
            EasyExcel.write(filePath, headClazz).registerWriteHandler(writeHandler).sheet(sheetNo, sheetName).doWrite(data);
        }
    
        /**
         * 按模板写文件(包含某些字段)
         *
         * @param filePath
         * @param headClazz   表头模板
         * @param data        数据
         * @param includeCols 包含字段集合,根据字段名称显示
         * @param sheetNo     sheet页号,从0开始
         * @param sheetName   sheet名称
         */
        public static void writeInclude(String filePath, Class headClazz, List data, Set<String> includeCols, Integer sheetNo, String sheetName) {
            EasyExcel.write(filePath, headClazz).includeColumnFiledNames(includeCols).sheet(sheetNo, sheetName).doWrite(data);
        }
    
        /**
         * 按模板写文件(排除某些字段)
         *
         * @param filePath
         * @param headClazz   表头模板
         * @param data        数据
         * @param excludeCols 过滤排除的字段,根据字段名称过滤
         * @param sheetNo     sheet页号,从0开始
         * @param sheetName   sheet名称
         */
        public static void writeExclude(String filePath, Class headClazz, List data, Set<String> excludeCols, Integer sheetNo, String sheetName) {
            EasyExcel.write(filePath, headClazz).excludeColumnFiledNames(excludeCols).sheet(sheetNo, sheetName).doWrite(data);
        }
    
        /**
         * 多个sheet页的数据链式写入
         * ExcelUtil.writeWithSheets(outputStream)
         * .writeModel(ExcelModel.class, excelModelList, "sheetName1")
         * .write(headData, data,"sheetName2")
         * .finish();
         *
         * @param outputStream
         */
        public static EasyExcelWriterFactory writeWithSheets(OutputStream outputStream) {
            EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(outputStream);
            return excelWriter;
        }
    
        /**
         * 多个sheet页的数据链式写入
         * ExcelUtil.writeWithSheets(file)
         * .writeModel(ExcelModel.class, excelModelList, "sheetName1")
         * .write(headData, data,"sheetName2")
         * .finish();
         *
         * @param file
         */
        public static EasyExcelWriterFactory writeWithSheets(File file) {
            EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(file);
            return excelWriter;
        }
    
        /**
         * 多个sheet页的数据链式写入
         * ExcelUtil.writeWithSheets(filePath)
         * .writeModel(ExcelModel.class, excelModelList, "sheetName1")
         * .write(headData, data,"sheetName2")
         * .finish();
         *
         * @param filePath
         */
        public static EasyExcelWriterFactory writeWithSheets(String filePath) {
            EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(filePath);
            return excelWriter;
        }
    
        /**
         * 多个sheet页的数据链式写入(失败了会返回一个有部分数据的Excel)
         * ExcelUtil.writeWithSheets(response, exportFileName)
         * .writeModel(ExcelModel.class, excelModelList, "sheetName1")
         * .write(headData, data,"sheetName2")
         * .finish();
         *
         * @param response
         * @param exportFileName 导出的文件名称
         */
        public static EasyExcelWriterFactory writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码
            String fileName = URLEncoder.encode(exportFileName, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(response.getOutputStream());
            return excelWriter;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426

    2.EasyExcelWriterFactory

    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.ExcelWriter;
    
    import java.io.File;
    import java.io.OutputStream;
    import java.util.List;
    
    
    public class EasyExcelWriterFactory {
    
        private int sheetNo = 0;
        private ExcelWriter excelWriter = null;
    
        public EasyExcelWriterFactory(OutputStream outputStream) {
            excelWriter = EasyExcel.write(outputStream).build();
        }
    
        public EasyExcelWriterFactory(File file) {
            excelWriter = EasyExcel.write(file).build();
        }
    
        public EasyExcelWriterFactory(String filePath) {
            excelWriter = EasyExcel.write(filePath).build();
        }
    
        /**
         * 链式模板表头写入
         *
         * @param headClazz 表头格式
         * @param data      数据 List 或者List>
         * @return
         */
        public EasyExcelWriterFactory writeModel(Class headClazz, List data, String sheetName) {
            excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(headClazz).build());
            return this;
        }
    
        /**
         * 链式自定义表头写入
         *
         * @param head
         * @param data      数据 List 或者List>
         * @param sheetName
         * @return
         */
        public EasyExcelWriterFactory write(List<List<String>> head, List data, String sheetName) {
            excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(head).build());
            return this;
        }
    
        /**
         * 使用此类结束后,一定要关闭流
         */
        public void finish() {
            excelWriter.finish();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    3.ExcelUtil

    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.net.URLEncoder;
    
    /**
     * @className com.yq.common.utils.ExcelUtil
     * @author: yangjie
     * @description: ExcelUtil
     */
    @Slf4j
    public class ExcelUtil {
        /**
         * 下载Excel
         *
         * @param response 请求response
         * @param fileName 下载文件名称 xxx.xlsx
         * @param filePath 下载文件路径 D://xxx/xxx
         */
        public static void downExcel(HttpServletResponse response, String fileName, String filePath) {
            // path是指想要下载的文件的路径
            File file = new File(filePath);
            ExcelUtil.downExcel(response, fileName, file);
        }
    
    
        /**
         * 下载Excel
         *
         * @param response 请求response
         * @param fileName 下载文件名称 xxx.xlsx
         * @param file     下载文件流
         */
        public static void downExcel(HttpServletResponse response, String fileName, File file) {
            FileInputStream fileInputStream = null;
            InputStream fis = null;
            OutputStream outputStream = null;
            try {
                // 将文件写入输入流
                fileInputStream = new FileInputStream(file);
                fis = new BufferedInputStream(fileInputStream);
                byte[] buffer = new byte[fis.available()];
                fis.read(buffer);
                fis.close();
                // 清空response
                response.reset();
                // 设置response的Header
                // 解决跨域问题,这句话是关键,对任意的域都可以,如果需要安全,可以设置成安前的域名
                response.addHeader("Access-Control-Allow-Origin", "*");
                response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
                response.setHeader("FileName", URLEncoder.encode(fileName, "UTF-8"));
                response.setHeader("Access-Control-Expose-Headers", "FileName");
                response.setCharacterEncoding("UTF-8");
                //Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
                //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
                // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                // 告知浏览器文件的大小
                response.addHeader("Content-Length", "" + file.length());
                outputStream = new BufferedOutputStream(response.getOutputStream());
                response.setContentType("application/octet-stream");
                outputStream.write(buffer);
                outputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
                log.error("文件下载异常,{}", e);
            } finally {
                if (fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    4.使用方式

    实体类:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class AiAssistantForecastVo implements Serializable {
        private static final long serialVersionUID = -2986024804753822180L;
        @ExcelProperty(value = "细分品类", index = 0)
        private String presentName;
        @ExcelProperty(value = "大区", index = 1)
        private String regionNo;
        @ExcelProperty(value = "管理城市", index = 2)
        private String managingCityNo;
        @ExcelProperty(value = "店铺", index = 3)
        private String organKey;
        @ExcelProperty(value = "指标", index = 4)
        private String indicator;
        @ExcelProperty(value = "202336", index = 5)
        private String naturalYearWeek202336;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    调用:

    String fileName = "xxxx_" + System.currentTimeMillis() + ".xlsx";
    String tempPath = "/home/uploads/";
    String filePath = tempPath + fileName;
    EasyExcelWriterFactory res = EasyExcelUtil.writeWithSheets(filePath)
    .writeModel(AiAssistantForecastVo.class, aiAssistantForecastVoList, "报表数据");
    res.finish();
    ExcelUtil.downExcel(response, fileName, filePath);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.如何处理不确定的字段?

    1. 通过反射拿到字段。
    2. 拿到字段后可以拿字段的注解。
    3. 根据字段注解的属性值可以确定需要填充的是哪个动态的字段。
    4. 通过使用 Reflect 反射工具类,很好的动态的填充了属性值。
    //判断,周数相等的时候,才能塞进去
    final Integer naturalYear = forecast.getNaturalYear();
    final Integer naturalYearWeek = forecast.getNaturalYearWeek();
    // 获取类的所有字段
    Field[] fields = aiAssistantForecastVo.getClass().getDeclaredFields();
    // 遍历字段
    for (Field field : fields) {
        field.setAccessible(true);
        // 判断字段上是否有指定的注解
        if (field.isAnnotationPresent(ExcelProperty.class)) {
            // 获取字段上的注解
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            // 获取注解的属性值
            final String[] value = annotation.value();
            final String property = value[0];
            if (StringUtils.equals(property, naturalYear.toString() + naturalYearWeek.toString())) {
                try {
                    field.set(aiAssistantForecastVo, Objects.nonNull(Reflect.on(forecast).field(mapValue).get()) ? Reflect.on(forecast).field(mapValue).get().toString() : "");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    6.需要注意的点

    • 如果是 docker 部署,记得设置挂载地址/home/uploads/
    #docker run的时候设置挂载地址
    -v /home/uploads:/home/uploads
    
    • 1
    • 2
    • 下载时间过长,可能需要设置 nginx 的超时时间
    http {
      proxy_connect_timeout 300; #单位秒
        proxy_send_timeout 300; #单位秒
        proxy_read_timeout 300; #单位秒
        proxy_buffer_size 16k;
        proxy_buffers 4 64k;
        proxy_busy_buffers_size 128k;
        proxy_temp_file_write_size 128k;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    觉得有用的话点个赞 👍🏻 呗。
    ❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

    💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

    🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

    img

  • 相关阅读:
    Sonic云真机学习总结6 - 1.4.1服务端、agent端部署
    Simple WPF: WPF实现一个MINIO等S3兼容对象存储上传文件的小工具
    数据结构--栈,队列
    linux系统编程入门
    docker启动paddlespeech服务,并使用接口调用
    【深度学习】 Python 和 NumPy 系列教程(廿六):Matplotlib详解:3、多子图和布局:subplots()函数
    新手入门:LoadRunner调用Analysis组件的相关技术点及知识总结
    MySQL高级十四:索引的基本使用
    字节跳动面试——算法
    jenkins自动化工具简介
  • 原文地址:https://blog.csdn.net/qyj19920704/article/details/134522162