【Java】POI和EasyExcel操作Excel及通用POI操作Excel工具类

前言

我们知道Excel在2003版本和2007版本有区别,03版本excel后缀未.xls,而07版本后缀未.xlsx,其次03版本最多只能有65536条记录,07版本的最多1048576条记录。
简单了解下Excel的简单组成:
在这里插入图片描述
整个excel文件叫做工作簿。有这些知识之后看下面的内容就很轻松了。

一、POI

API文档地址:https://tool.oschina.net/apidocs/apidoc?api=apache-POI
中文文档参照:https://www.cnblogs.com/fqfanqi/p/6172223.html
maven依赖信息:

    <dependencies>
        <!--03版本Excel(xls)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>
        <!--07版本Excel(xlsx)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
        <!--日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.1</version>
        </dependency>
        <!--单元测试工具-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

在使用中,主要是创建工作簿使用的实现类不一样。

2003版本的excel

  • 大文件写HSSF
  • 优点:过程中写入缓存,不操作磁盘,最后一次写入磁盘,速度快
  • 缺点:最多只能处理65536行,否则抛出异常

2007版本excel

  • 大文件写XSSF
  • 优点:可以写入较大的数据量,20w…
  • 缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,100w条…

2007版本excel

  • 大文件写SXSSF(XSSF的加速版本)
  • 优点:可以写入较大的数据量,100w+,写数据速度快,占更少的内存
  • 注意:过程中会产生临时文件,需要清理零时文件
  • 默认由100条记录被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件
  • 如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)

  SXSSFWorkbook来自官方的解释:实现BigGridDemo策略的流式XSSFWorkbook版本,这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。请注意:仍然可能会消耗大量的内存,这些内存基于您正在使用的功能,例如合并区域,注释…仍然只存储在内存中,因此,如果广泛使用,可能需要大量内存。

需要注意的是Excel中下标都是从0开始的。

写文件代码(一个方法对应一次测试):

package com.dl;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ExcelWriteTest {
    
    
    private String Path="E:\\5120154230JavaCode\\POIAndEasyExcel\\POI\\"; //文件保存路径

    /**
     * 03版本的excel
     * 大文件写HSSF
     * 优点:过程中写入缓存,不操作磁盘,最后一次写入磁盘,速度快
     * 缺点:最多只能处理65536行,否则抛出异常
     * @throws IOException
     */
    public void testWrite03() throws IOException {
    
    
        //1、创建一个工作簿
        Workbook workbook=new HSSFWorkbook();
        //2、创建一个工作表(可以选择按照下标或者名字创建)
        Sheet sheet=workbook.createSheet("统计表1");
        //3、创建一个行
        Row row1=sheet.createRow(0);
        //4、创建一个单元格
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("王小二");
        Cell cell12 = row1.createCell(1);
        cell12.setCellValue("tonghua");
        Row row2=sheet.createRow(1);
        Cell cell21 = row2.createCell(1);
        cell21.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
        FileOutputStream fileOutputStream = new FileOutputStream(Path + "tonghua统计表1.xls");
        workbook.write(fileOutputStream);
        fileOutputStream.close();
    }

    /**
     * 07版本excel
     * 大文件写XSSF
     * 优点:可以写入较大的数据量,20w...
     * 缺点:写数据时速度非常慢,非常耗内存,也会发生内存移除,100w条...
     * @throws IOException
     */
    public void testWrite07() throws IOException {
    
    
        //1、创建一个工作簿
        Workbook workbook=new XSSFWorkbook();
        //2、创建一个工作表
        Sheet sheet=workbook.createSheet("统计表1");
        //3、创建一个行
        Row row1=sheet.createRow(0);
        //4、创建一个单元格
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("王小二");
        Cell cell12 = row1.createCell(1);
        cell12.setCellValue("tonghua");
        Row row2=sheet.createRow(1);
        Cell cell21 = row2.createCell(1);
        cell21.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
        FileOutputStream fileOutputStream = new FileOutputStream(Path + "tonghua统计表1.xlsx");
        workbook.write(fileOutputStream);
        fileOutputStream.close();
    }
    public void testWrite03BigData() throws IOException {
    
    
        long start=System.currentTimeMillis();
        //1、创建一个工作簿
        Workbook workbook=new HSSFWorkbook();
        //2、创建一个工作表
        Sheet sheet=workbook.createSheet("统计表1");
        //3、创建一个行
        for(int row=0;row<65536;row++){
    
    
            Row row1 = sheet.createRow(row);
            for (int cell=0;cell<10;cell++){
    
    
                Cell cell1 = row1.createCell(cell);
                cell1.setCellValue(cell);
            }
        }
        FileOutputStream fileOutputStream = new FileOutputStream(Path + "BigData03.xls");
        workbook.write(fileOutputStream);
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(double)(end-start)/1000+"秒!");
        fileOutputStream.close();
    }

    /**
     * 07版本耗时多
     * @throws IOException
     */
    public void testWrite07BigData() throws IOException {
    
    
        long start=System.currentTimeMillis();
        //1、创建一个工作簿
        Workbook workbook=new XSSFWorkbook();
        //2、创建一个工作表
        Sheet sheet=workbook.createSheet("统计表1");
        //3、创建一个行
        for(int row=0;row<65536;row++){
    
    
            Row row1 = sheet.createRow(row);
            for (int cell=0;cell<10;cell++){
    
    
                Cell cell1 = row1.createCell(cell);
                cell1.setCellValue(cell);
            }
        }
        FileOutputStream fileOutputStream = new FileOutputStream(Path + "BigData07.xlsx");
        workbook.write(fileOutputStream);
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(double)(end-start)/1000+"秒!");
        fileOutputStream.close();
    }
    public void testWrite07BigDataS() throws IOException {
    
    
        long start=System.currentTimeMillis();
        //1、创建一个工作簿
        Workbook workbook=new SXSSFWorkbook();
        //2、创建一个工作表
        Sheet sheet=workbook.createSheet("统计表1");
        //3、创建一个行
        for(int row=0;row<65555;row++){
    
    
            Row row1 = sheet.createRow(row);
            for (int cell=0;cell<10;cell++){
    
    
                Cell cell1 = row1.createCell(cell);
                cell1.setCellValue(cell);
            }
        }
        FileOutputStream fileOutputStream = new FileOutputStream(Path + "BigDataS07.xlsx");
        workbook.write(fileOutputStream);
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(double)(end-start)/1000+"秒!");
        fileOutputStream.close();
        ((SXSSFWorkbook)workbook).dispose();//清除临时文件
    }
    /**
     * 07版本excel
     * 大文件写SXSSF
     * 优点:可以写入较大的数据量,100w+,写数据速度快,占更少的内存
     * 注意:过程中会产生临时文件,需要清理零时文件
     * 默认由100条记录被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件
     * 如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)
     *
     * SXSSFWorkbook来自官方的解释:实现BigGridDemo策略的流式XSSFWorkbook版本,这允许写入非常大的文件而不会耗尽内存
     * 因为任何时候只有可配置的行部分被保存在内存中。
     * 请注意:仍然可能会消耗大量的内存,这些内存基于您正在使用的功能,例如合并区域,注释...仍然只存储在内存中,因此,如果
     * 广泛使用,可能需要大量内存。
     * @throws IOException
     */
    public void testWrite0707() throws IOException {
    
    
        //1、创建一个工作簿
        Workbook workbook=new SXSSFWorkbook();
        //2、创建一个工作表
        Sheet sheet=workbook.createSheet("统计表1");
        //3、创建一个行
        Row row1=sheet.createRow(0);
        //4、创建一个单元格
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("王小二");
        Cell cell12 = row1.createCell(1);
        cell12.setCellValue("dignli");
        Row row2=sheet.createRow(1);
        Cell cell21 = row2.createCell(1);
        cell21.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss"));
        FileOutputStream fileOutputStream = new FileOutputStream(Path + "tonghua统计表1.xlsx");
        workbook.write(fileOutputStream);
        fileOutputStream.close();
    }
    public static void main(String[] args) throws IOException {
    
    
        ExcelWriteTest excelWriteTest=new ExcelWriteTest();
//        excelWriteTest.testWrite03BigData();
        excelWriteTest.testWrite07BigDataS();
//        excelWriteTest.testWrite07();

    }
}

读文件代码

package com.dl;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;

import static org.apache.poi.ss.usermodel.CellType.*;

public class ExcelReadTest {
    
    
    private String Path="E:\\5120154230JavaCode\\POIAndEasyExcel\\POI\\tonghua统计表1.xls";
    @Test
    public void test03Read() throws IOException {
    
    
        FileInputStream fileInputStream=new FileInputStream(Path);
        Workbook workbook=new HSSFWorkbook(fileInputStream);
        Sheet sheet=workbook.getSheetAt(0);
        Row row = sheet.getRow(0);
        //注意获取值的类型
        System.out.println(row.getCell(0).getStringCellValue());
        System.out.println(row.getCell(1).getNumericCellValue());
        fileInputStream.close();
    }

    private String PATH="E:\\5120154230JavaCode\\POIAndEasyExcel\\POI\\tonghua统计表1.xlsx";
    @Test
    public void testCellType() throws IOException {
    
    
        FileInputStream fileInputStream=new FileInputStream(PATH);
        if(fileInputStream==null){
    
    
            System.out.println("文件不存在!");
        }
        Workbook workbook= new XSSFWorkbook(fileInputStream);
        Sheet sheetAt = workbook.getSheetAt(0);
        if(sheetAt!=null){
    
    
            //标题
            Row rowTitle = sheetAt.getRow(0);
            int cells = rowTitle.getPhysicalNumberOfCells();
            for (int cell = 0; cell < cells; cell++) {
    
    
                if(rowTitle!=null){
    
    
                    System.out.print(rowTitle.getCell(cell)+"||");
                }
            }
            System.out.println();
            //标题下的各个单元格内容
            int rows=sheetAt.getPhysicalNumberOfRows();//得到行数
            for(int row=1;row<rows;row++){
    
    
                Row rowData=sheetAt.getRow(row);
                if(rowData!=null){
    
    
                    int cellDataNums=rowData.getPhysicalNumberOfCells();
                    for(int cellDataNum=0;cellDataNum<cellDataNums;cellDataNum++){
    
    
                        System.out.print(row+"--"+cellDataNum);
                        Cell cellData = rowData.getCell(cellDataNum);
                        if(cellData!=null){
    
    
//                            System.out.println(cellData);
                            CellType cellTypeEnum = cellData.getCellTypeEnum();
                            String cellValue="";
                            switch (cellTypeEnum){
    
     //判断单元格内容类型
                                case STRING:
                                    System.out.print("[String] ");
                                    cellValue=cellData.getStringCellValue();
                                    break;
                                case BLANK:
                                    System.out.print("[BLANK] ");
                                    break;
                                case BOOLEAN:
                                    System.out.print("[Boolean] ");
                                    cellValue=String.valueOf(cellData.getBooleanCellValue());
                                    break;
                                case NUMERIC: //数字
                                    System.out.print("[Numeric] ");
                                    if(HSSFDateUtil.isCellDateFormatted(cellData)){
    
    
                                        Date date=cellData.getDateCellValue();
                                        cellValue=new DateTime(date).toString("yyyy-MM-dd HH:mm:ss");
                                    }else{
    
    
                                        cellValue=String.valueOf(cellData.getNumericCellValue());
                                        BigDecimal db = new BigDecimal(cellValue);
                                        cellValue = db.toPlainString();
                                    }
                                    break;
                                case ERROR:
                                    System.out.print("ERROR");
                                    break;
                            }
                            System.out.println(cellValue);
                        }
                    }
                }
            }
        }
        fileInputStream.close(); //关闭文件输入流
    }

}

二、EasyExcel

云雀地址:https://www.yuque.com/easyexcel/doc/easyexcel
Maven依赖:

<!--需要注意的是easyexcel中包含了03版本和07版本的poi依赖,额外导入poi依赖需要删除掉,否则可能出现jar包冲突-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.7</version>
</dependency>
<!--单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!--JSON序列化工具-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.74</version>
</dependency>
<!--POJO实体类插件-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

<!--日期格式化工具-->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.1</version>
</dependency>

lombok还需要在idea中安装lombok插件。

在这里插入图片描述
这个操作直接看云雀上的教程即可,非常简单。

POI操作Excel通用工具类

Maven依赖:

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.7</version>
        </dependency>
        <!--里面已经包含了POI相关依赖,不排除则会依赖冲突-->
        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<!--        <dependency>-->
<!--            <groupId>org.apache.poi</groupId>-->
<!--            <artifactId>poi</artifactId>-->
<!--            <version>4.0.1</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.poi</groupId>-->
<!--            <artifactId>poi-ooxml</artifactId>-->
<!--            <version>4.0.1</version>-->
<!--        </dependency>-->

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.74</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <!--日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
    </dependencies>

首先创建一个注解用于读取实体类属性。

1、自定义注解

package com.dl.Utils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CellField {
    
    

    /**
     * 文件列名
     */
    public String name() default "";

}

2、实体类

package com.dl.POJO;

import com.dl.Utils.CellField;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

/**
 * 文件信息实体类
 */
public class IPOFileInfo {
    
    

    /**文件所属类型*/
    @CellField(name = "文件类型")
    private String fileType;

    /**文件所属编码 */
    @CellField(name = "文件编码")
    private String fileCode;

    /**文件名称 */
    @CellField(name = "文件名称")
    private String fileName;

    /**文件URL */
    @CellField(name = "文件URL")
    private String fileUrl;

    /**文件日期 */
    @CellField(name = "文件日期")
    private String fileDate;

    /**返回的文件id */
    @CellField(name = "文件id")
    private String fileId;

    public String getFileType() {
    
    
        return fileType;
    }

    public void setFileType(String fileType) {
    
    
        this.fileType = fileType;
    }

    public String getFileCode() {
    
    
        return fileCode;
    }

    public void setFileCode(String fileCode) {
    
    
        this.fileCode = fileCode;
    }

    public String getFileName() {
    
    
        return fileName;
    }

    public void setFileName(String fileName) {
    
    
        this.fileName = fileName;
    }

    public String getFileUrl() {
    
    
        return fileUrl;
    }

    public void setFileUrl(String fileUrl) {
    
    
        this.fileUrl = fileUrl;
    }

    public String getFileDate() {
    
    
        return fileDate;
    }

    public void setFileDate(String fileDate) {
    
    
        this.fileDate = fileDate;
    }

    public String getFileId() {
    
    
        return fileId;
    }

    public void setFileId(String fileId) {
    
    
        this.fileId = fileId;
    }

    @Override
    public String toString() {
    
    
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

3、工具类

package com.dl.Utils;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;



/**
 * Excel工具类
 */
public final class ExcelUtils {
    
    

    /** 日志对象 **/
    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelUtils.class);

    /** 私有无参构造方法 **/
    private ExcelUtils() {
    
     }

    /**
     * 生成excel2007文档
     *
     * @param filePath 文件保存路径 ,例如:D:/excel/test.xlsx
     * @param beans 实体对象
     */
    public static <T> void createExcel(String filePath, List<T> beans) throws FileNotFoundException {
    
    
        String fileName = null;
        if(filePath.contains("/")){
    
    
            fileName=filePath.substring(filePath.lastIndexOf('/') + 1);
        }else if(filePath.contains("\\")){
    
    
            fileName=filePath.substring(filePath.lastIndexOf('\\') + 1);
        }else{
    
    
            fileName="sheet1";
        }
        Workbook wb = createWorkbook(beans, fileName);
        File file = createFile(filePath);
        try (OutputStream os = new FileOutputStream(file)) {
    
    
            wb.write(os);
        } catch (IOException e) {
    
    
            LOGGER.error("生成excel2007文件失败", e);
        }
    }

    /**
     * 导出excel2007文件
     *
     * @param response 响应对象
     * @param beans 实体对象集合
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> beans) {
    
    
        // 以当前时间作为文件名
        String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        // 创建Workbook对象
        Workbook wb = createWorkbook(beans, fileName);
        // 导出excel
        try (OutputStream os = response.getOutputStream()) {
    
    
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            wb.write(os);
        } catch (IOException e) {
    
    
            LOGGER.error("导出excel2007文件失败", e);
        }
    }

    /**
     * 导出excel2007文件
     *
     * @param response 响应对象
     * @param beans 实体对象集合
     * @param fileName 文件名称 ,如:测试名称
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> beans, String fileName) {
    
    
        // 创建Workbook对象
        Workbook wb = createWorkbook(beans, fileName);
        // 导出excel
        try (OutputStream os = response.getOutputStream()) {
    
    
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            wb.write(os);
        } catch (IOException e) {
    
    
            LOGGER.error("导出excel2007文件失败", e);
        }
    }

    /**
     * 导出excel2007模板
     *
     * @param response 响应对象
     * @param clazz<T> 类类型
     */
    public static <T> void exportExcelTemplate(HttpServletResponse response, Class<T> clazz) {
    
    
        // 以当前时间作为文件名
        String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        exportExcelTemplate(response, clazz, fileName);
    }

    /**
     * 导出excel2007模板
     *
     * @param response 响应对象
     * @param clazz<T> 类类型
     * @param fileName 文件名称
     */
    public static <T> void exportExcelTemplate(HttpServletResponse response, Class<T> clazz, String fileName) {
    
    
        // 1.创建2007版工作簿
        Workbook wb = new XSSFWorkbook();
        // 2.创建工作表
        Sheet sheet = wb.createSheet(fileName);
        // 3.获取实体类标有@CellField注解的Field对象
        List<Field> fields = new ArrayList<Field>();
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
    
    
            CellField anno = field.getAnnotation(CellField.class);
            if (anno != null) {
    
    
                fields.add(field);
            }
        }
        // 4.设置列宽
        for (int i = 0; i < fields.size(); i++) {
    
    
            sheet.setColumnWidth((short) i, (short) (20.7 * 150));
        }
        // 5.写入标题
        Row row = sheet.createRow(0);
        writeTitles(fields, row);
        // 6.导出excel模板
        try (OutputStream os = response.getOutputStream()) {
    
    
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            wb.write(os);
        } catch (IOException e) {
    
    
            LOGGER.error("导出excel2007模板失败", e);
        } finally {
    
    
            if (wb != null) {
    
    
                try {
    
    
                    wb.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 解析Excel文档,转成对象集合返回
     *
     * @param filePath 文件存放路径,例如:D:/excel/test.xlsx
     * @param clazz 泛型类类型
     * @return List<T> 对象集合
     */
    public static <T> List<T> parseExcel(String filePath, Class<T> clazz) {
    
    
        List<T> list = new ArrayList<>();
        InputStream input = null;
        try {
    
    
            input = new FileInputStream(new File(filePath));
            if (input != null && !input.markSupported()) {
    
    
                input = new PushbackInputStream(input, 8);
            }
            Workbook wb = getWorkbook(input,filePath);
//            final XSSFWorkbook wb = new XSSFWorkbook(input);
            if (wb != null) {
    
    
                list = getBeanList(wb, clazz);
            }
        } catch (Exception e) {
    
    
            LOGGER.error("解析文件失败", e);
        } finally {
    
    
            if (input != null) {
    
    
                try {
    
    
                    input.close();
                } catch (IOException e) {
    
    
                    LOGGER.error("关闭流失败", e);
                }
            }
        }
        return list;
    }

    /**
     * 删除该目录下所有文件
     * @param filePath 文件目录路径,如:d:/test/
     * @return boolean
     */
    public static boolean deleteFiles(String filePath) {
    
    
        File file = new File(filePath);
        if (file.exists()) {
    
    
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
    
    
                for (File f : files) {
    
    
                    if (f.isFile() && f.delete()) {
    
    
                        LOGGER.info("删除" + f.getName() + "文件成功");
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * 删除指定文件
     * @param filePath 文件目录路径,如:d:/test/
     * @param fileName 文件名称,如:test.xlsx
     * @return boolean
     */
    public static boolean deleteFile(String filePath, String fileName) {
    
    
        File file = new File(filePath);
        if (file.exists()) {
    
    
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
    
    
                for (File f : files) {
    
    
                    if (f.isFile() && f.getName().equals(fileName)) {
    
    
                        return f.delete();
                    }
                }
            }
        }
        return false;
    }

    /**
     * 获取Workbook对象
     *
     * @param input 输入流
     * @return Workbook
     */
    private static Workbook getWorkbook(InputStream input,String filePath) {
    
    
        try {
    
    
//            if (POIFSFileSystem.hasPOIFSHeader(input)) {
    
    
//                return new HSSFWorkbook(input); // 得到2003工作簿
//            } else {
    
    
//                try (OPCPackage op = OPCPackage.open(input)) {
    
    
//                    return new XSSFWorkbook(op); // 得到2007工作簿
//                } catch (Exception e) {
    
    
//                    LOGGER.error("获取Workbook对象失败", e);
//                }
//            }
            if(filePath.endsWith(".xls")){
    
      //03版本
                return new HSSFWorkbook(input);
            }else{
    
     //07版本
                return new XSSFWorkbook(input);
            }
        } catch (IOException e) {
    
    
            LOGGER.error("获取Workbook对象失败", e);
        }
        return null;
    }

    /**
     * 通过泛型转换为对象集合
     *
     * @param wb 工作簿对象
     * @param clazz 实体对象类类型
     * @param <T> 泛型类型
     * @return <T> 泛型实体对象
     */
    private static <T> List<T> getBeanList(Workbook wb, Class<T> clazz) {
    
    
        List<T> list = new ArrayList<T>();
        // 注解名称与字段属性的对应关系
        Map<String, Field> annoMap = getFields(clazz);
        // 列所引与标题名称对应关系
        Map<Integer, String> titleMap = new HashMap<Integer, String>();
        Sheet sheet = wb.getSheetAt(0); // 获取第一张工作表
        Iterator<Row> rows = sheet.iterator(); // 利用迭代器,取出每一个行
        int index = 0;
        while (rows.hasNext()) {
    
    
            Row row = rows.next(); // 每一行
            Iterator<Cell> cells = row.iterator(); // 利用迭代器,取出每一个格
            if (index == 0) {
    
    
                // 获取第一行标题
                while (cells.hasNext()) {
    
    
                    Cell cell = cells.next(); // 每一格
                    titleMap.put(cell.getColumnIndex(), cell.getStringCellValue().trim());
                }
            } else {
    
    
                // 创建实体对象,把值设置进去
                if (!annoMap.isEmpty() && !titleMap.isEmpty()) {
    
    
                    list.add(buildBean(annoMap, titleMap, clazz, cells));
                }
            }
            index++;
        }
        return list;
    }

    /**
     * 注解名称与字段属性的对应关系
     *
     * @param clazz 实体对象类类型
     * @return Map<String,Field>
     */
    private static <T> Map<String, Field> getFields(Class<T> clazz) {
    
    
        Map<String, Field> annoMap = new HashMap<String, Field>();
        Field[] fileds = clazz.getDeclaredFields();
        for (Field filed : fileds) {
    
    
            CellField cellField = filed.getAnnotation(CellField.class);
            if (cellField != null && StringUtils.isNotBlank(cellField.name())) {
    
    
                annoMap.put(cellField.name(), filed);
            }
        }
        return annoMap;
    }

    /**
     * 创建实体,设置值
     *
     * @param annoMap 注解名称与字段属性的对应的Map
     * @param titleMap excel列所引与标题名称对应的Map
     * @param clazz 实体对象类类型
     * @param cells 每一行的所有格子
     * @return List<T>
     */
    private static <T> T buildBean(Map<String, Field> annoMap, Map<Integer, String> titleMap, Class<T> clazz, Iterator<Cell> cells) {
    
    
        T t = null;
        try {
    
    
            t = clazz.newInstance();
            while (cells.hasNext()) {
    
    
                Cell cell = cells.next(); // 每一格
                String title = titleMap.get(cell.getColumnIndex());
                if (annoMap.containsKey(title)) {
    
    
                    Field field = annoMap.get(title);
                    Class<?> valType = field.getType();
                    Object value = getType(cell.getStringCellValue(), valType);
                    String fieldName = field.getName();
                    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    Method method = ReflectionUtils.findMethod(clazz, methodName, valType);
                    if (method != null) {
    
    
                        ReflectionUtils.invokeMethod(method, t, value);
                    }
                }
            }
        } catch (Exception e) {
    
    
            LOGGER.error("创建泛型实体对象失败", e);
        }
        return t;
    }

    /**
     * 转换成实体属性对应的类型
     *
     * @param value 每一格的数值
     * @param valType 实体属性类型
     * @return Object 转换为对应类型以obj返回
     */
    private static <T> Object getType(String value, Class<T> valType) {
    
    
        try {
    
    
            if (valType == String.class) {
    
    
                return String.valueOf(value);
            } else if (valType == Date.class) {
    
    
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  // 默认格式
                return sdf.parse(value);
            } else if (valType == Double.class) {
    
    
                return Double.parseDouble(value);
            }else if (valType == BigDecimal.class) {
    
    
                return new BigDecimal(value);
            } else if (valType == Integer.class) {
    
    
                return Integer.parseInt(value);
            } else if (valType == Long.class) {
    
    
                return Long.parseLong(value);
            } else if (valType == Boolean.class) {
    
    
                return Boolean.parseBoolean(value);
            }
        } catch (Exception e) {
    
    
            LOGGER.error("类型转换异常", e);
        }
        return value;
    }

    /**
     * 创建Workbook对象
     *
     * @param beans 实体集合对象
     * @param fileName 文件名
     * @return Workbook
     */
    private static <T> Workbook createWorkbook(List<T> beans, String fileName) {
    
    
        // 1.创建2007版工作簿
        Workbook wb = new XSSFWorkbook();
        // 2.创建工作表
        Sheet sheet = wb.createSheet(fileName);
        // 3.获取实体类标有@CellField注解的Field对象
        List<Field> fields = getFields(beans);
        // 4.设置列宽
        for (int i = 0; i < fields.size(); i++) {
    
    
            sheet.setColumnWidth((short) i, (short) (20.7 * 150));
        }
        // 5.写入标题
        Row row = sheet.createRow(0);
        writeTitles(fields, row);
        // 6.写入内容
        writeContents(beans, fields, sheet);
        return wb;
    }

    /**
     * 创建文件对象
     *
     * @param filePath 保存路径
     * @return File
     */
    private static File createFile(String filePath) {
    
    
        File file = null;
        try {
    
    
            // 创建文件目录
            if(filePath.contains("/")){
    
    
                file=new File(filePath.substring(0, filePath.lastIndexOf('/')));
            }else if(filePath.contains("\\")){
    
    
                file= new File(filePath.substring(0, filePath.lastIndexOf('\\')));
            }else{
    
    
                file= new File(Thread.currentThread().getContextClassLoader().getResource("").getPath().substring(1));
            }
            if (!file.exists()) {
    
    
                file.mkdirs();
            }
            // 创建文件路径
            file = new File(filePath);
            if (!file.exists() && file.createNewFile()) {
    
    
                LOGGER.info("创建文件对象成功");
            }
        } catch (IOException e) {
    
    
            LOGGER.error("创建文件对象失败", e);
        }
        return file;
    }

    /**
     * 写入标题
     *
     * @param fields Field对象集合
     * @param row 行对象
     */
    private static void writeTitles(List<Field> fields, Row row) {
    
    
        if (fields != null && !fields.isEmpty()) {
    
    
            for (int i = 0; i < fields.size(); i++) {
    
    
                CellField cellField = fields.get(i).getAnnotation(CellField.class);
                if (cellField == null) {
    
    
                    continue;
                }
                Cell cell = row.createCell(i);
                // 获取带有name属性的值并写入
                if (StringUtils.isNotBlank(cellField.name())) {
    
    
                    cell.setCellValue(cellField.name());
                }
            }
        }
    }

    /**
     * 写入内容
     *
     * @param beans 实体对象集合
     * @param fields Field对象集合
     * @param sheet 工作表对象
     * @param <T> 泛型类
     */
    private static <T> void writeContents(List<T> beans, List<Field> fields, Sheet sheet) {
    
    
        for (int i = 0; i < beans.size(); i++) {
    
    
            T t = beans.get(i);
            Row row = sheet.createRow(i + 1);
            for (int j = 0; j < fields.size(); j++) {
    
    
                CellField cellField = fields.get(j).getAnnotation(CellField.class);
                if (cellField != null) {
    
    
                    Class<?> valType = fields.get(j).getType(); // 获取属性类型
                    String fieldName = fields.get(j).getName(); // 获取属性名
                    String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    Method method = ReflectionUtils.findMethod(t.getClass(), methodName);
                    setValues(method, t, j, valType, cellField, row); // 设置值
                }
            }
        }
    }

    /**
     * 设置值
     *
     * @param method 方法对象
     * @param t 泛型对象
     * @param j 下标
     * @param valType 类型对象
     * @param cellField 注解对象
     * @param row 行对象
     * @param <T> 泛型
     */
    private static <T> void setValues(Method method, T t, int j, Class<?> valType, CellField cellField, Row row) {
    
    
        if (method != null) {
    
    
            Object value = ReflectionUtils.invokeMethod(method, t);
            if (value != null && valType == Date.class) {
    
     // 默认日期类型格式:yyyy-MM-dd HH:mm:ss
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                row.createCell(j).setCellValue(sdf.format(((Date) value).getTime()));
            } else {
    
     // 字符串
                row.createCell(j).setCellValue(value == null ? "" : String.valueOf(value));
            }
        }
    }

    /**
     * 获取标有@CellField注解的Field对象
     *
     * @param beans 对象集合
     * @return List<Field>
     */
    private static <T> List<Field> getFields(List<T> beans) {
    
    
        Class<? extends Object> cls = beans.get(0).getClass();
        Field[] declaredFields = cls.getDeclaredFields();
        List<Field> annoFields = new ArrayList<Field>();
        // 筛选出标有注解的字段
        for (Field field : declaredFields) {
    
    
            CellField anno = field.getAnnotation(CellField.class);
            if (anno != null) {
    
    
                annoFields.add(field);
            }
        }
        return annoFields;
    }

}

4、测试类

package com.dl;

import com.dl.POJO.IPOFileInfo;
import com.dl.Utils.ExcelUtils;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

/**
 * excel工具类测试
 */
public class TestExcelUtils {
    
    

    public static void main(String[] args) throws FileNotFoundException {
    
    
        List<IPOFileInfo> list = new ArrayList<>();
        IPOFileInfo info = new IPOFileInfo();
        info.setFileType("1");
        info.setFileCode("001");
        info.setFileDate("2020-12-26");
        info.setFileId("111");
        info.setFileName("测试文档1");
        info.setFileUrl("www.baidu.com");
        list.add(info);

        IPOFileInfo info2 = new IPOFileInfo();
        info2.setFileType("2");
        info2.setFileCode("002");
        info2.setFileDate("2020-12-26");
        info2.setFileId("222");
        info2.setFileName("测试文档2");
        info2.setFileUrl("www.baidu.com");
        list.add(info2);

        IPOFileInfo info3 = new IPOFileInfo();
        info3.setFileType("3");
        info3.setFileCode("003");
        info3.setFileDate("2020-12-26");
        info3.setFileId("333");
        info3.setFileName("测试文档3");
        info3.setFileUrl("www.baidu.com");
        list.add(info3);
        // 生成excel2007文档
//        ExcelUtils.createExcel("E:/5120154230JavaCode/POIAndEasyExcel/EasyExcel/tonghua.xlsx", list);

        // 解析excel文档
        List<IPOFileInfo> results = ExcelUtils.parseExcel("E:/5120154230JavaCode/POIAndEasyExcel/EasyExcel/tonghua.xlsx", IPOFileInfo.class);
        for (IPOFileInfo ipoFileInfo : results) {
    
    
            System.out.println(ipoFileInfo);
        }
    }
}

在这里插入图片描述

参考文章:java操作excel文档通用工具类

猜你喜欢

转载自blog.csdn.net/dl962454/article/details/114643343