Java操作Excel

 一、小册子(原生POI)

gitee地址:https://gitee.com/zhang-zhixi/springboot-poi-excel.git

GitHub地址:https://github.com/zhangzhixi0305/springboot-poi-excel.git

1、新建SpringBoot项目导入Pom

<dependencies>
    <!--对Excel操作使用到的jar包-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
</dependencies>

2、进行上传Excel的表单数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="/importExcel" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="file"/><br>
    <input type="submit" value="上传"/>
</form>

</body>
</html>

3、处理Excel上传下载请求的Controller

package com.zhixi.controller;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author zhangzhixi
 * @version 1.0
 * @date 2021-12-28 16:36
 */
@RestController
public class ExcelController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping("/exportExcel")
    public void exportExcel(HttpServletResponse response) throws Exception {
        // 模拟从数据库查询数据
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student(1L, "周深(web导出)", 28, "贵州", new SimpleDateFormat("yyyy-MM-dd").parse("1992-9-29"), 161.0, true));
        studentList.add(new Student(2L, "李健(web导出)", 46, "哈尔滨", new SimpleDateFormat("yyyy-MM-dd").parse("1974-9-23"), 174.5, true));
        studentList.add(new Student(3L, "周星驰(web导出)", 58, "香港", new SimpleDateFormat("yyyy-MM-dd").parse("1962-6-22"), 174.0, false));

        // 读取模板(实际开发可以放在resources文件夹下,随着项目一起打包发布)
        InputStream excelInputStream = new ClassPathResource("static/excel/student_info.xlsx").getInputStream();
        // XSSFWorkbook除了直接接收Path外,还可以传入输入流
        XSSFWorkbook workbook = new XSSFWorkbook(excelInputStream);
        // 获取模板sheet
        XSSFSheet sheet = workbook.getSheetAt(0);
        // 找到数据起始行(前两行是标题和表头,要跳过,所以是getRow(2))
        XSSFRow dataTemplateRow = sheet.getRow(2);
        // 构造一个CellStyle数组,用来存放单元格样式。一行有N个单元格,数组初始长度就设置为N
        CellStyle[] cellStyles = new CellStyle[dataTemplateRow.getLastCellNum()];
        for (int i = 0; i < cellStyles.length; i++) {
            // 收集每一个格子对应的格式,你可以理解为准备了一把“格式刷”
            cellStyles[i] = dataTemplateRow.getCell(i).getCellStyle();
        }

        // 创建单元格,并设置样式和数据
        for (int i = 0; i < studentList.size(); i++) {
            // 注意是i+2,模板前两行是大标题和表头。你可能看着难受,想把上面for的i改为i+2,千万别。因为studentList必须从0开始取值
            XSSFRow row = sheet.createRow(i + 2);
            // 为每一行创建单元格并设置数据
            Student student = studentList.get(i);

            XSSFCell nameCell = row.createCell(0);// 创建单元格
            nameCell.setCellValue(student.getName());         // 设置值
            nameCell.setCellStyle(cellStyles[0]);             // 设置单元格样式

            XSSFCell ageCell = row.createCell(1);
            ageCell.setCellValue(student.getAge());
            ageCell.setCellStyle(cellStyles[1]);

            XSSFCell addressCell = row.createCell(2);
            addressCell.setCellValue(student.getAddress());
            addressCell.setCellStyle(cellStyles[2]);

            /**
             * 你可能有疑问,这里是日期类型,是不是要和上一次一样,设置单元格样式为日期类型?
             * 这回不用了,因为上面已经拷贝了模板的样式,生日一栏就是按日期类型展示的
             */
            XSSFCell birthdayCell = row.createCell(3);
            birthdayCell.setCellValue(student.getBirthday());
            birthdayCell.setCellStyle(cellStyles[3]);

            XSSFCell heightCell = row.createCell(4);
            heightCell.setCellValue(student.getHeight());
            heightCell.setCellStyle(cellStyles[4]);

            XSSFCell mainLandChinaCell = row.createCell(5);
            mainLandChinaCell.setCellValue(student.getIsMainlandChina());
            mainLandChinaCell.setCellStyle(cellStyles[5]);
        }

        /**
         * 之前通过本地文件流输出到桌面:
         * FileOutputStream out = new FileOutputStream("/Users/kevin/Documents/study/student_info_export.xlsx");
         * 现在用网络流:response.getOutputStream()
         * 注意,response的响应流没必要手动关闭,交给Tomcat关闭
         */
        String fileName = new String("学生信息表.xlsx".getBytes("UTF-8"), "ISO-8859-1");
        response.setContentType("application/octet-stream");
        response.setHeader("content-disposition", "attachment;filename=" + fileName);
        response.setHeader("filename", fileName);
        workbook.write(response.getOutputStream());
        workbook.close();
        logger.info("导出学生信息表成功!");
    }

    @PostMapping("/importExcel")
    public Map importExcel(MultipartFile file) throws Exception {
        // 直接获取上传的文件流,传入构造函数
        XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());
        // 获取工作表。一个工作薄中可能有多个工作表,比如sheet1 sheet2,可以根据下标,也可以根据sheet名称。这里根据下标即可。
        XSSFSheet sheet = workbook.getSheetAt(0);

        // 收集每一行数据(跳过标题和表头,所以int i = 2)
        int lastRowNum = sheet.getLastRowNum();
        List<Student> studentList = new ArrayList<>();
        for (int i = 2; i <= lastRowNum; i++) {
            // 收集当前行所有单元格的数据
            XSSFRow row = sheet.getRow(i);
            short lastCellNum = row.getLastCellNum();
            List<String> cellDataList = new ArrayList<>();
            for (int j = 0; j < lastCellNum; j++) {
                cellDataList.add(getValue(row.getCell(j)));
            }

            // 把当前行数据设置到POJO。由于Excel单元格的顺序和POJO字段顺序一致,也就是数据类型一致,所以可以直接强转
            Student student = new Student();
            student.setName(cellDataList.get(0));
            student.setAge(Integer.parseInt(cellDataList.get(1)));
            student.setAddress(cellDataList.get(2));
            // getValue()方法返回的是字符串类型的 1962-6-22 00:00:00,这里按"yyyy-MM-dd HH:mm:ss"重新解析为Date
            student.setBirthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(cellDataList.get(3)));
            student.setHeight(Double.parseDouble(cellDataList.get(4)));
            student.setHeight(Double.parseDouble(cellDataList.get(4)));
            student.setIsMainlandChina(Boolean.valueOf(cellDataList.get(5)));
            studentList.add(student);
        }

        // 插入数据库
        saveToDB(studentList);
        logger.info("导入{}成功!", file.getOriginalFilename());

        Map<String, Object> result = new HashMap<>();
        result.put("code", 200);
        result.put("data", null);
        result.put("msg", "success");
        return result;
    }

    private void saveToDB(List<Student> studentList) {
        if (CollectionUtils.isEmpty(studentList)) {
            return;
        }
        // 直接打印,模拟插入数据库
        studentList.forEach(System.out::println);
    }

    /**
     * 提供POI数据类型 --> Java数据类型的转换
     * 由于本方法返回值设为String,所以不管转换后是什么Java类型,都要以String格式返回
     * 所以Date会被格式化为yyyy-MM-dd HH:mm:ss
     * 后面根据需要自己另外转换
     *
     * @param cell
     * @return
     */
    private String getValue(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getRichStringCellValue().getString().trim();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    // DateUtil是POI内部提供的日期工具类,可以把原本是日期类型的NUMERIC转为Java的Data类型
                    Date javaDate = DateUtil.getJavaDate(cell.getNumericCellValue());
                    String dateString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(javaDate);
                    return dateString;
                } else {
                    /*
                     * 无论Excel中是58还是58.0,数值类型在POI中最终都被解读为Double。
                     * 这里的解决办法是通过BigDecimal先把Double先转成字符串,如果是.0结尾,把.0去掉
                     * */
                    String strCell = "";
                    Double num = cell.getNumericCellValue();
                    BigDecimal bd = new BigDecimal(num.toString());
                    if (bd != null) {
                        strCell = bd.toPlainString();
                    }
                    // 去除 浮点型 自动加的 .0
                    if (strCell.endsWith(".0")) {
                        strCell = strCell.substring(0, strCell.indexOf("."));
                    }
                    return strCell;
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
        }
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class Student {
        private Long id;
        private String name;
        private Integer age;
        private String address;
        private Date birthday;
        private Double height;
        private Boolean isMainlandChina;
    }

}

二、狂神说Java(原生POI)

读操作用到的Excel表:会员消费商品明细表

1、创建一个普通的Java项目,导入依赖

<dependencies>
    <!--原生POI依赖-->
    <!--xLs(03)-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.2</version>
    </dependency>
    <!--xLsx(07)-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.2</version>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

2、简要说明 

HSSFWorkbook :对应 Excel 03 版本,最多支持65535行

XSSFWorkbook:对应 Excel 07 版本,行数无限制

SXSSFWorkbook:缓存版本的操作Excel方式。

03版最多支持到65536行,而07版不受限制,理论上无限

3、案例测试

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @ClassName ExcelWriter
 * @Author zhangzhixi
 * @Description POI操作Excel
 * @Date 2022/8/29 13:06
 * @Version 1.0
 */
public class ExcelWriterReadTest {

    /**
     * 将Excel文件写入到什么地方
     */
    public final static String WRITER_PATH = "F:\\StudyProject\\狂神说\\Excel操作\\zhjixi-poi\\src\\main\\resources\\";

    /**
     * 写入Excel-03版本
     */
    @Test
    public void excelTarget03() {
        // 1、创建工作簿
        Workbook workbook = new HSSFWorkbook();
        // 2、创建工作表
        Sheet sheet = workbook.createSheet("考核成绩表");
        // 3、创建一行
        Row row1 = sheet.createRow(0);
        // 4、创建一个列(一个单元格)
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("语文");

        Cell cell12 = row1.createCell(1);
        cell12.setCellValue("数学");

        Cell cell13 = row1.createCell(2);
        cell13.setCellValue("英语");


        // 创建一行
        Row row2 = sheet.createRow(1);
        Cell cell21 = row2.createCell(0);
        Cell cell22 = row2.createCell(1);
        Cell cell23 = row2.createCell(2);
        cell21.setCellValue("75");
        cell22.setCellValue("70");
        cell23.setCellValue("66");

        // 5、生成工作簿
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表03.xls");
            workbook.write(fileOutputStream);
            System.out.println("工作表生成成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                workbook.close();
                assert fileOutputStream != null;
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 写入Excel-07版本
     */
    @Test
    public void excelTarget07() {
        // 1、创建工作簿
        Workbook workbook = new XSSFWorkbook();
        // 2、创建工作表
        Sheet sheet = workbook.createSheet("考核成绩表");
        // 3、创建一行
        Row row1 = sheet.createRow(0);
        // 4、创建一个列(一个单元格)
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("语文");

        Cell cell12 = row1.createCell(1);
        cell12.setCellValue("数学");

        Cell cell13 = row1.createCell(2);
        cell13.setCellValue("英语");


        // 创建一行
        Row row2 = sheet.createRow(1);
        Cell cell21 = row2.createCell(0);
        Cell cell22 = row2.createCell(1);
        Cell cell23 = row2.createCell(2);
        cell21.setCellValue("75");
        cell22.setCellValue("70");
        cell23.setCellValue("66");

        // 5、生成工作簿
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表07.xlsx");
            workbook.write(fileOutputStream);
            System.out.println("工作表生成成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                workbook.close();
                assert fileOutputStream != null;
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 写入大数据量Excel-03版本
     */
    @Test
    public void excelTargetBigData03() {
        long beginTime = System.currentTimeMillis();
        // 1、创建工作簿
        Workbook workbook = new HSSFWorkbook();
        // 2、创建工作表
        Sheet sheet = workbook.createSheet("考核成绩表");
        for (int rowNum = 0; rowNum < 6553; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int coluNum = 0; coluNum < 10; coluNum++) {
                Cell cell = row.createCell(coluNum);
                cell.setCellValue(coluNum);
            }
        }

        // 5、生成工作簿
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表大数据写入03.xls");
            workbook.write(fileOutputStream);
            System.out.println("工作表生成成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                workbook.close();
                assert fileOutputStream != null;
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("耗时:" + ((double) (System.currentTimeMillis()) - beginTime) / 1000 + "s");
    }

    /**
     * 写入大数据量Excel-07-普通版
     * 写入10w条数据:9.149s
     */
    @Test
    public void excelTargetBigData07() {
        long beginTime = System.currentTimeMillis();
        // 1、创建工作簿
        Workbook workbook = new XSSFWorkbook();
        // 2、创建工作表
        Sheet sheet = workbook.createSheet("考核成绩表");
        for (int rowNum = 0; rowNum < 100000; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int coluNum = 0; coluNum < 10; coluNum++) {
                Cell cell = row.createCell(coluNum);
                cell.setCellValue(coluNum);
            }
        }

        // 5、生成工作簿
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表大数据写入07.xlsx");
            workbook.write(fileOutputStream);
            System.out.println("工作表生成成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                workbook.close();
                assert fileOutputStream != null;
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.out.println("耗时:" + ((double) (System.currentTimeMillis()) - beginTime) / 1000 + "s");
    }

    /**
     * 写入大数据量Excel-07-缓存版本
     * 写入10w条数据:2.161s
     */
    @Test
    public void excelTargetBigDataCache07() {
        long beginTime = System.currentTimeMillis();
        // 1、创建工作簿
        Workbook workbook = new SXSSFWorkbook();
        // 2、创建工作表
        Sheet sheet = workbook.createSheet("考核成绩表");
        for (int rowNum = 0; rowNum < 100000; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int coluNum = 0; coluNum < 10; coluNum++) {
                Cell cell = row.createCell(coluNum);
                cell.setCellValue(coluNum);
            }
        }

        // 5、生成工作簿
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(WRITER_PATH + "考核成绩表大数据写入Cache07.xls");
            workbook.write(fileOutputStream);
            System.out.println("工作表生成成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                workbook.close();
                assert fileOutputStream != null;
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("耗时:" + ((double) (System.currentTimeMillis()) - beginTime) / 1000 + "s");
    }

    /**
     * 读取文件并判断列的类型
     *
     * @throws Exception 异常
     */
    @Test
    public void testCellType() throws Exception {

        //获取文件流
        FileInputStream fis = new FileInputStream(WRITER_PATH + "会员消费商品明细表.xls");

        //创建一个工作簿。使用 excel能操作的这边他都可以操作
        Workbook workbook = new HSSFWorkbook(fis);
        Sheet sheet = workbook.getSheetAt(0);

        //获取标题内容
        Row rowTitle = sheet.getRow(0);
        if (rowTitle != null) {
            //得到一行有多少列有数据
            int cellCount = rowTitle.getPhysicalNumberOfCells();
            for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                Cell cell = rowTitle.getCell(cellNum);
                if (cell != null) {
                    String cellValue = cell.getStringCellValue();
                    System.out.print(cellValue + "|");
                }
            }
            System.out.println();
        }

        // 遍历所有行
        int rowCount = sheet.getPhysicalNumberOfRows();
        for (int rowNum = 1; rowNum < rowCount; rowNum++) {
            Row rowData = sheet.getRow(rowNum);

            // 遍历一行中的所有列
            if (rowData != null) {
                assert rowTitle != null;
                int cellCount = rowTitle.getPhysicalNumberOfCells();
                for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                    System.out.print("[" + (rowNum + 1) + "-" + (cellNum + 1) + "]");

                    Cell cell = rowData.getCell(cellNum);
                    //匹配列的数据类型
                    if (cell != null) {
                        CellType cellType = cell.getCellType();
                        String cellValue = "";
                        switch (cellType) {
                            //字符
                            case STRING:
                                System.out.print("【 String】");
                                cellValue = cell.getStringCellValue();
                                break;
                            //布尔
                            case BOOLEAN:
                                System.out.print("【 BOOLEAN】");
                                cellValue = String.valueOf(cell.getBooleanCellValue());
                                break;
                            //空
                            case BLANK:
                                System.out.print("【 BLANK】");
                                break;
                            //数字(日期、普通数字)
                            case NUMERIC:
                                System.out.print("【 NUMERIC】");
                                // 日期
                                if (DateUtil.isCellDateFormatted(cell)) {
                                    System.out.print("--【日期】");
                                    LocalDateTime localDateTimeCellValue = cell.getLocalDateTimeCellValue();
                                    cellValue = localDateTimeCellValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                                } else {
                                    //不是日期格式,防止数字过长!
                                    System.out.print("--【转换为字符串输出】");
                                    cell.setCellType(CellType.STRING);
                                    cellValue = cell.toString();
                                }
                                break;
                            //错误
                            case ERROR:
                                System.out.print("【 数据类型错误】");
                                break;
                            default:
                                break;
                        }
                        System.out.println(cellValue);
                    }
                }
            }
        }
        //关闭流
        fis.close();
    }


    public static int f(String s1, String s2) {
        if (s1.length() == 0 || s2.length() == 0) {
            return 0;
        }

        if (s1.charAt(0) == s2.charAt(0)) {
            return f(s1.substring(1), s2.substring(1)) + 1;
        } else {
            return Math.max(f(s1.substring(1), s2), f(s1, s2.substring(1)));
        }
    }

}

三、EasyExcel(推荐)

官方文档地址:https://easyexcel.opensource.alibaba.com/docs/current/

自己跟着文档练习即可。

导入Maven坐标:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.1</version>
</dependency>

四、POI操作:使用自定义注解补充信息(Excel导出)

创建Maven项目,依赖如下

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.2</version>
    </dependency>
</dependencies>

定义注解

/**
 * @author zhixi
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@Documented
public @interface Excel {

    /**
     * 字段别名
     *
     * @return 字段名称
     */
    String name() default "";

    /**
     * 字典值
     *
     * @return 字典值,用逗号分割,比如:"0=女,1=男"
     */
    String dictValue() default "";
}

实体类

public class User {

    @Excel(name = "用户ID")
    private String id;

    @Excel(name = "姓名")
    private String name;

    @Excel(name = "年龄")
    private int age;

    /**
     * 性别 0-女 1-男
     */
    @Excel(name = "性别", dictValue = "0=女,1=男")
    private int gender;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }

    public User(String name, int age, int gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public User(String id, String name, int age, int gender) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
}

导出Excel

package com.zhixi;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ExcelExporter {


    public static void export(List<User> users, OutputStream outputStream) {
        try (Workbook workbook = new XSSFWorkbook()) {
            // 创建一个工作表
            Sheet sheet = workbook.createSheet();

            // 获取所有的属性
            Field[] declaredFields = User.class.getDeclaredFields();
            // 获取所有包含Excel注解的字段
            List<Field> filteredFields = Stream.of(declaredFields)
                    .filter(f -> f.isAnnotationPresent(Excel.class))
                    .collect(Collectors.toList());

            // 写入标题行
            writeTitleRow(workbook,sheet, filteredFields);

            // 写入数据行
            for (int i = 0; i < users.size(); i++) {
                User user = users.get(i);
                // 数据从第二行开始写入(一行是标题)
                Row userRow = sheet.createRow(i + 1);
                writeDataRow(userRow, user, filteredFields);
            }

            // 写入文件
            workbook.write(outputStream);
        } catch (IOException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    private static void writeTitleRow(Workbook workbook,Sheet sheet, List<Field> filteredFields) {
        // 创建新行
        Row row = sheet.createRow(0);
        // 设置表头加粗
        CellStyle cellStyle = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        cellStyle.setFont(font);

        for (int i = 0; i < filteredFields.size(); i++) {
            // 在所在行内创建新单元格
            Cell cell = row.createCell(i);
            Excel excel = filteredFields.get(i).getAnnotation(Excel.class);
            cell.setCellValue(excel.name());
            cell.setCellStyle(cellStyle);
        }
    }

    private static void writeDataRow(Row userRow, User user, List<Field> filteredFields) throws IllegalAccessException {

        // 构造字段的值字典(处理dictValue这个属性)
        Map<Field, Map<String, String>> fieldDict = buildFieldDict(filteredFields);

        for (int i = 0; i < filteredFields.size(); i++) {
            // 在行上面创建列,i表示列的下标
            Cell cell = userRow.createCell(i);
            Field field = filteredFields.get(i);
            field.setAccessible(true);
            // dictValue这个属性不为空,就从map字典中取出对应的值
            Excel annotation = field.getAnnotation(Excel.class);
            if (StringUtils.hasLength(annotation.dictValue())) {
                String key = String.valueOf(field.get(user));
                cell.setCellValue(fieldDict.get(field).get(key));
            } else {
                cell.setCellValue(String.valueOf(field.get(user)));
            }
            field.setAccessible(false);
        }

    }

    private static Map<Field, Map<String, String>> buildFieldDict(List<Field> filteredFields) {
        Map<Field, Map<String, String>> fieldDict = new HashMap<>();

        for (Field field : filteredFields) {
            Excel annotation = field.getAnnotation(Excel.class);
            if (StringUtils.hasLength(annotation.dictValue())) {
                String d = annotation.dictValue();
                String[] kvs = d.split(",");
                Map<String, String> map = new HashMap<>();
                for (String kv : kvs) {
                    String[] split = kv.split("=");
                    map.put(split[0], split[1]);
                }
                fieldDict.put(field, map);
            }
        }
        return fieldDict;
    }

    public static void main(String[] args) throws IOException {
        List<User> users = new ArrayList<>();
        users.add(new User("1", "张三", 20, 1));
        users.add(new User("2", "李四", 30, 0));
        users.add(new User("3", "王五", 40, 1));
        System.out.println(Paths.get(".").getFileName());
        OutputStream outputStream = Files.newOutputStream(Paths.get("users.xlsx"));
        ExcelExporter.export(users, outputStream);
    }
}

 

posted @ 2021-12-28 17:32  Java小白的搬砖路  阅读(518)  评论(0编辑  收藏  举报