框架篇-easyexcel使用

EasyExcel

1.簡介

傳統(tǒng)Excel操作或者解析都是利用Apach POI進行操作,但是使用過這個框架的人都知道,這個框架并不完美,有較多的缺陷:

  • 使用步驟繁瑣
  • 動態(tài)寫出Excel操作非常麻煩
  • 對于新手來說,很難在短時間內(nèi)上手
  • 讀寫時需要占用較大的內(nèi)容,當數(shù)據(jù)量大時容器發(fā)生OOM

基于上述原因,阿里開源出一款易上手,且比較節(jié)省內(nèi)存的Excel操作框架:EasyExcel

官網(wǎng)文檔

image-20201225142524599

源碼地址

image-20201225142812075

2.對比

  • 讀取

    image-20201225143206677

    從上圖可知:

    • POI

      當利用POI去讀取Excel時,首先會將數(shù)據(jù)全部加載到內(nèi)存中,然后返回給調(diào)用者

      當數(shù)據(jù)量比較大時,及其容易發(fā)生OOM

    • EasyExcel

      POI 不用的是,EasyExcel主要是采用sax模式一行一行解析,并將一行的解析結(jié)果以觀察者的模式通知處理,即使數(shù)據(jù)量較大時也不會發(fā)生OOM,以下是其讀取數(shù)據(jù)原理圖

      image-20201225143933780

      這樣即使數(shù)據(jù)量比較大時也不會發(fā)生OOM,節(jié)省了內(nèi)存的開銷,以下是其讀取數(shù)據(jù)64M內(nèi)存1分鐘內(nèi)讀取75M(46W行25列)的Excel 內(nèi)存開銷圖

      image-20201225144238809
  • 維護

    • 當其他開源框架去使用時,步驟復雜,EasyExcel上手及其簡單

    • 其他開源框架存在一些BUG修復不及時,官方文檔舉了一個例子,如下:

      image-20201225144632555

3.API

3.1 寫操作

3.1.1 簡單寫

  • 準備工作

    創(chuàng)建springboot項目(easyexcel) pom.xml內(nèi)容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.7.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.briup</groupId>
        <artifactId>easyexcel</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>easyexcel</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </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>
            <!-- easyexcel 依賴 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>2.2.7</version>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    

    修改application.properties文件,內(nèi)容如下:

    server.port=9991
    

    新增POJO

    package com.briup.easyexcel.pojo;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    @Data
    public class Student implements Serializable {
        private Integer id;
        private String name;
        private Double salary;
        private Date birthday;
    }
    
  • 簡單寫操作

    • 操作

      通過EasyExcel這個工具類,即可完成寫操作,如下:

      在測試類中進行代碼測試

      public List<Student> getData() {
          List<Student> lists = new ArrayList<>();
          for(int i = 0; i <= 10; i++) {
              Student student = new Student();
              student.setId(i + 1);
              student.setName("李四" + i);
              student.setBirthday(new Date());
              student.setSalary(1500.00D);
              lists.add(student);
          }
          return lists;
      }
      
      @Test
      void contextLoads() {
          EasyExcel.write("學生信息表.xlsx", Student.class).sheet().doWrite(getData());
      }
      
      

      執(zhí)行測試方法,結(jié)果會在本地產(chǎn)生一個excel文件

      image-20201225152247751

      如下:
      image-20201225152340467
    • 代碼解釋:

      image-20201225152644631

      圖中紅框選中部分表示:

      EasyExcel.write 表示構(gòu)建一個Excel寫對象,其參數(shù)含義為:

      • 第一個參數(shù): 寫出表格的文件名
      • 第二個參數(shù):寫到表格數(shù)據(jù)類型的class對象

      查看EasyExcel源碼,其所有write方法源碼如下:

      public class EasyExcelFactory {
          /**
           * 構(gòu)建一個Excel寫對象
           *
           * @return
           */
          public static ExcelWriterBuilder write() {
              return new ExcelWriterBuilder();
          }
      
          /**
           * 構(gòu)建一個Excel寫對象
           *
           * @param file 用來寫出文件對象
           *            
           * @return Excel writer builder
           */
          public static ExcelWriterBuilder write(File file) {
              return write(file, null);
          }
      
          /**
           * 構(gòu)建 Excel寫對象
           *
           * @param file
           *           用來寫出的文件對象
           * @param head
           *          寫出的數(shù)據(jù)類型的class對象
           * @return Excel writer builder
           */
          public static ExcelWriterBuilder write(File file, Class head) {
              ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
              excelWriterBuilder.file(file);
              if (head != null) {
                  excelWriterBuilder.head(head);
              }
              return excelWriterBuilder;
          }
      
          /**
           * 構(gòu)建Excel 寫對象
           *   
           * @param pathName
           *           寫出的文件路徑名
           * @return Excel writer builder
           */
          public static ExcelWriterBuilder write(String pathName) {
              return write(pathName, null);
          }
      
          /**
           * 構(gòu)建excel 寫對象
           *
           * @param pathName
           *            寫出的文件路徑名
           * @param head
           *            寫出數(shù)據(jù)的數(shù)據(jù)類型的class對象
           * @return Excel writer builder
           */
          public static ExcelWriterBuilder write(String pathName, Class head) {
              ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
              excelWriterBuilder.file(pathName);
              if (head != null) {
                  excelWriterBuilder.head(head);
              }
              return excelWriterBuilder;
          }
      
          /**
           * 構(gòu)建excel寫對象
           *
           * @param outputStream
           *            寫出的輸出流對象
           * @return Excel writer builder
           */
          public static ExcelWriterBuilder write(OutputStream outputStream) {
              return write(outputStream, null);
          }
      
          /**
           * 構(gòu)建excel寫對象
           *
           * @param outputStream
           *           寫出的輸出流
           * @param head
           *           寫出數(shù)據(jù)的數(shù)據(jù)類型的class對象
           * @return Excel writer builder
           */
          public static ExcelWriterBuilder write(OutputStream outputStream, Class head) {
              ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
              excelWriterBuilder.file(outputStream);
              if (head != null) {
                  excelWriterBuilder.head(head);
              }
              return excelWriterBuilder;
          }
      }
      
      

      通過源碼知道,在構(gòu)建Excel寫對象時可以通過多種方式構(gòu)建,具體使用哪種看具體的需求。

      image-20201225154431692

      如上圖,sheet()代表要在excel 那個sheet頁寫入數(shù)據(jù),如果不指定,默認在第一個sheet頁寫入數(shù)據(jù),其sheet頁的值為:0

      image-20201225154735583

      當然也可以手動指定在哪個sheet頁,關(guān)于sheet源碼如下:

      public class ExcelWriterBuilder extends AbstractExcelWriterParameterBuilder<ExcelWriterBuilder, WriteWorkbook> {
        
          /*
              選中第一個sheet頁
              寫操作 sheet 頁的值為 0
          */
          public ExcelWriterSheetBuilder sheet() {
              return sheet(null, null);
          }
      
          /*
              選中 第一個的 sheet頁
              sheet 頁的名字 為 sheetNo
          */  
          public ExcelWriterSheetBuilder sheet(Integer sheetNo) {
              return sheet(sheetNo, null);
          }
      
          /*
              選中第一個的 sheet頁
              sheet 頁的名字 為 sheetName
          */  
          public ExcelWriterSheetBuilder sheet(String sheetName) {
              return sheet(null, sheetName);
          }
      
          /*
              選中第一個 sheet頁
              sheet 頁的名字 為 sheetNo 或者 sheetName
          */
          public ExcelWriterSheetBuilder sheet(Integer sheetNo, String sheetName) {
              ExcelWriter excelWriter = build();
              ExcelWriterSheetBuilder excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);
              if (sheetNo != null) {
                  excelWriterSheetBuilder.sheetNo(sheetNo);
              }
              if (sheetName != null) {
                  excelWriterSheetBuilder.sheetName(sheetName);
              }
              return excelWriterSheetBuilder;
          }
      }
      
      image-20201225155746393

      doWrite表示寫出的數(shù)據(jù),寫出的數(shù)據(jù)為List集合

3.1.2 復雜寫

  • 自定義表頭

    image-20201225160019872

    如上圖,之前寫出的數(shù)據(jù),表頭均為屬性名,且列的順序為類中屬性的順序,但是在實際開發(fā)過程中,表頭為自定義信息,且順序也不一定按照屬性的順序來。

    因此需要自定義表頭信息,具體實現(xiàn)如下:

    修改POJO類,內(nèi)容如下:

    public class Student implements Serializable {
    
        @ExcelProperty("學生編號")
        private Integer id;
    
        @ExcelProperty("學生姓名")
        private String name;
    
        @ExcelProperty("學生薪水")
        private Double salary;
    
        @ExcelProperty("學生生日")
        private Date birthday;
    }
    

    @ExcelProperty 就是用來指定表頭信息,再次執(zhí)行之前的測試類方法,如下:

    image-20201225160926467

    當然如果想要自定義列的順序時,可以修改POJO,如下:

    @Data
    public class Student implements Serializable {
    
        @ExcelProperty(value = "學生編號",order = 10)
        private Integer id;
    
        @ExcelProperty(value = "學生姓名",order = 2)
        private String name;
    
        @ExcelProperty(value = "學生薪水",order = 1)
        private Double salary;
    
        @ExcelProperty(value = "學生生日",order = 11)
        private Date birthday;
    }
    

    再次執(zhí)行方法,order的值越大,列越往右,如下:

    image-20201225161443127

有時候更多的時候需要在表頭上,在加上一個表頭,例如為學生信息

修改POJO類如下:

@Data
public class Student implements Serializable {

    @ExcelProperty(value = {"學生信息","學生編號"},order = 10)
    private Integer id;

    @ExcelProperty(value = {"學生信息","學生姓名"},order = 2)
    private String name;

    @ExcelProperty(value = {"學生信息","學生薪水"},order = 1)
    private Double salary;

    @ExcelProperty(value = {"學生信息","學生生日"},order = 11)
    private Date birthday;
}

再去執(zhí)行之前的測試方式,內(nèi)容如下:

image-20201225162211889
  • 列寬,行高定義

    從上述例子可知,之前操作,產(chǎn)生的列的寬度與內(nèi)容的寬度并沒有對應(yīng),所以需要手動指定寬度,修改POJO類如下:

    @HeadRowHeight(value = 35) // 表頭行高
    @ContentRowHeight(value = 25) // 內(nèi)容行高
    @ColumnWidth(value = 50) // 列寬
    @Data
    public class Student implements Serializable {
    
        @ExcelProperty(value = {"學生信息","學生編號"},order = 10)
        private Integer id;
    
        @ExcelProperty(value = {"學生信息","學生姓名"},order = 2)
        private String name;
    
        @ExcelProperty(value = {"學生信息","學生薪水"},order = 1)
        private Double salary;
    
        @ExcelProperty(value = {"學生信息","學生生日"},order = 11)
        private Date birthday;
    }
    
    

    執(zhí)行測試方法,內(nèi)容如下:

    image-20201225162911641

    雖然上述方式能夠修改列寬,但是寬度太大,實際開發(fā)中更希望根據(jù)內(nèi)容自適應(yīng)寬度,步驟如下:

    修改POJO類,將列寬注解去掉

    image-20201225163305882

    修改測試方法,內(nèi)容如下:

    @Test
    void contextLoads() {
        EasyExcel.write("學生信息表.xlsx", Student.class)
            // 自適應(yīng)寬度,但是這個不是特別精確
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
            .sheet()
            .doWrite(getData());
    }
    

    執(zhí)行測試方法,內(nèi)容如下:

    image-20201225163405488

    注意:這個自適應(yīng)寬度,不是特別精確

  • 日期格式化

    從上述例子中,發(fā)現(xiàn)日期格式都是固定的格式,但是有時候需要自定義格式,因此可以修改POJO類達到以下效果,如下:

    image-20201225163817239

    執(zhí)行測試方法,結(jié)果如下:

    image-20201225163846055

3.1.3 忽略寫

實際開發(fā)過程中,并不像把所有的屬性數(shù)據(jù)全部寫出,那么可以修改POJO類,增加@ExcelIgnore注解進行忽略,如下:

image-20201225164245033

執(zhí)行測試方法,如下:

image-20201225164421398

3.1.4 指定寫

上述"忽略寫"例子中,可以指定哪些屬性不輸出到Excel表格中,但是這個種方式是固定的,更多的時候需要動態(tài)指定哪些輸出,哪些不輸出。實現(xiàn)步驟如下:

POJO類:

@HeadRowHeight(value = 35) // 表頭行高
@ContentRowHeight(value = 25) // 內(nèi)容行高
@Data
public class Student implements Serializable {

    @ExcelProperty(value = {"學生信息","學生編號"},order = 10)
    private Integer id;

    @ExcelProperty(value = {"學生信息","學生姓名"},order = 2)
    private String name;

    @ExcelProperty(value = {"學生信息","學生薪水"},order = 1)
    private Double salary;

    @ExcelProperty(value = {"學生信息","學生生日"},order = 11)
    @DateTimeFormat("yyyy-MM-dd")
    private Date birthday;
}

測試方法:

@Test
void contextLoads() {
    // 設(shè)置 要導出列的屬性名
    // 必須要跟類型的屬性名保持一致
    Set<String> set = new HashSet<>();
    set.add("id");
    set.add("name");

    EasyExcel.write("學生信息表.xlsx", Student.class)
        .includeColumnFiledNames(set)
        // 自適應(yīng)寬度,但是這個不是特別精確
        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
        .sheet()
        .doWrite(getData());
}

執(zhí)行測試方法,內(nèi)容如下:

image-20201225165744896

3.2.讀操作

3.2.1 簡單讀

  • 簡介

    讀取在實際開發(fā)中也占據(jù)了較大地位,但是讀取并不是讀取任意的一個Excel文件,而是讀取按照事先提供好的Excel模板,用戶在模塊版上修改數(shù)據(jù)的Excel

  • 準備POJO類

    package com.briup.easyexcel.pojo;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.annotation.format.DateTimeFormat;
    import com.alibaba.excel.annotation.write.style.ContentRowHeight;
    import com.alibaba.excel.annotation.write.style.HeadRowHeight;
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    @Data
    public class Student_Read implements Serializable {
    
        @ExcelProperty(value = {"學生信息","學生編號"})
        private Integer id;
    
        @ExcelProperty(value = {"學生信息","學生姓名"})
        private String name;
    
        @ExcelProperty(value = {"學生信息","學生薪水"})
        private Double salary;
    
        @ExcelProperty(value = {"學生信息","學生生日"})
        private Date birthday;
    }
    
    

    如上:如果使用該類的對象去裝載Excel中的數(shù)據(jù),那么讀取時就只能讀取以下樣式的

    Excel數(shù)據(jù),否則數(shù)據(jù)部分丟失或者全部丟失

    image-20201228105825344
  • 準備Excel文件,內(nèi)容如下:

    這里我是放到項目的根路徑下

    image-20201228105915389
  • 在測試類中準備測試方法,用來讀取

    @Test
    void readExcel() throws Exception {
    
        List<Student_Read> list =  new ArrayList<>();
    
        /*
         * EasyExcel 讀取 是基于SAX方式
         * 因此在解析時需要傳入監(jiān)聽器
         */
        // 第一個參數(shù) 為 excel文件路徑
        // 讀取時的數(shù)據(jù)類型
        // 監(jiān)聽器
        EasyExcel.read("學生信息表" + ExcelTypeEnum.XLSX.getValue(), Student_Read.class, new AnalysisEventListener<Student_Read>() {
    
            // 每讀取一行就調(diào)用該方法
            @Override
            public void invoke(Student_Read data, AnalysisContext context) {
                list.add(data);
            }
    
            // 全部讀取完成就調(diào)用該方法
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                System.out.println("讀取完成");
            }
        }).sheet().doRead();
    
        list.forEach(System.out::println);
    }
    
  • 執(zhí)行測試方法,結(jié)果如下:

    image-20201228111624149

以下是對測試方法代碼的解釋(started)

image-20201228142133510

如上圖:

EasyExcel.read 該方法是用來創(chuàng)建ExcelReaderBuilder對象,該對象就是用來解析Excel文檔

read方法需要傳入三個參數(shù),其具體含義如下:

  • 第一個參數(shù)

    需要解析文件的路徑,當然除了傳入一個文件路徑以外,還可以傳入InputStream

    源碼如下:

    image-20201228144555456
  • 第二參數(shù)

    數(shù)據(jù)類型的Class類型對象,可以不傳

  • 第三個參數(shù)

    事件監(jiān)聽器,在之前介紹這款框架時說過,該框架是基于SAX的一種解析,加載一行數(shù)據(jù)到內(nèi)存就會去解析一行,主要是為了節(jié)約內(nèi)存。

    image-20201228144958463

    invoke方法代表每解析一行就會調(diào)用一次,data數(shù)據(jù)表示解析出來一行的數(shù)據(jù)

    doAfterAllAnalysed 該方法表示將所有數(shù)據(jù)解析完畢以后才會去調(diào)用該方法

sheet方法代表讀取excel第幾個sheet,常用sheet方法如下:

image-20201228145757740

用法與之前寫的用法類似,這里就不再過多介紹

doRead方法代表開始讀取excel數(shù)據(jù)

4.2 其他讀

實際開發(fā)中一般讀操作用的最多的就是簡單讀,如果實在是有復雜的需求,例如讀取表頭或者是調(diào)用Excel里面的公式,可以參照官方文檔

4.WEB

上述例子中,讀寫操作就是在本地去操作Excel文檔,實際開發(fā)中都是在web中,但是其實用法都是一樣的,只不過數(shù)據(jù)的來源不一樣,這里提供了一個Excel工具類,其中就包括web操作。如下:

package com.briup.server.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.briup.server.exception.SMSException;
import com.briup.server.logging.LogHolder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

public class ExcelUtil {
    /**
     * 寫出一個 excel 文件到本地
     * <br />
     * 將類型所有加了 @ExcelProperty 注解的屬性全部寫出
     *
     * @param fileName  文件名 不要后綴
     * @param sheetName sheet名
     * @param data      寫出的數(shù)據(jù)
     * @param clazz     要寫出數(shù)據(jù)類的Class類型對象
     * @param <T>       寫出的數(shù)據(jù)類型
     */
    public static <T> void writeExcel(String fileName, String sheetName, List<T> data, Class<T> clazz) {
        writeExcel(null, fileName, sheetName, data, clazz);
    }


    /**
     * 按照指定的屬性名進行寫出 一個 excel
     *
     * @param attrName  指定的屬性名 必須與數(shù)據(jù)類型的屬性名一致
     * @param fileName  文件名 不要后綴
     * @param sheetName sheet名
     * @param data      要寫出的數(shù)據(jù)
     * @param clazz     要寫出數(shù)據(jù)類的Class類型對象
     * @param <T>       要寫出的數(shù)據(jù)類型
     */
    public static <T> void writeExcel(Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {
        fileName = StringUtils.isBlank(fileName) ? "學生管理系統(tǒng)" : fileName;
        sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;

        try(FileOutputStream fos = new FileOutputStream(fileName)) {
            write(fos,attrName,sheetName,data,clazz);
        } catch (Exception exception) {
            exception.printStackTrace();
         
        }


    }

    /**
     * 讀取 指定格式的 excel文檔
     *
     * @param fileName 文件名
     * @param clazz    數(shù)據(jù)類型的class對象
     * @param <T>      數(shù)據(jù)類型
     * @return
     */
    public static <T> List<T> readExcel(String fileName, Class<T> clazz) {
        
        return readExcel(fileName, clazz, null);
    }

    /**
     * 取 指定格式的 excel文檔
     * 注意一旦傳入自定義監(jiān)聽器,則返回的list為空,數(shù)據(jù)需要在自定義監(jiān)聽器里面獲取
     *
     * @param fileName     文件名
     * @param clazz        數(shù)據(jù)類型的class對象
     * @param readListener 自定義監(jiān)聽器
     * @param <T>          數(shù)據(jù)類型
     * @return
     */
    public static <T> List<T> readExcel(String fileName, Class<T> clazz, ReadListener<T> readListener) {
  

        try(FileInputStream fis = new FileInputStream(fileName)) {
            return read(fis,clazz,readListener);
        } catch (Exception exception) {
            exception.printStackTrace();
        
        }
    }


    /**
     * 導出  一個 excel
     *         導出excel所有數(shù)據(jù)
     * @param response
     * @param fileName  件名 最好為英文,不要后綴名
     * @param sheetName sheet名
     * @param data      要寫出的數(shù)據(jù)
     * @param clazz     要寫出數(shù)據(jù)類的Class類型對象
     * @param <T>       要寫出的數(shù)據(jù)類型
     */
    public static <T> void export(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz) {
        export(response, null, fileName, sheetName, data, clazz);
    }

    /**
     * 按照指定的屬性名進行寫出 一個 excel
     *
     * @param response
     * @param attrName  指定的屬性名 必須與數(shù)據(jù)類型的屬性名一致
     * @param fileName  文件名 最好為英文,不要后綴名
     * @param sheetName sheet名
     * @param data      要寫出的數(shù)據(jù)
     * @param clazz     要寫出數(shù)據(jù)類的Class類型對象
     * @param <T>       要寫出的數(shù)據(jù)類型
     */
    public static <T> void export(HttpServletResponse response, Set<String> attrName, String fileName, String sheetName, List<T> data, Class<T> clazz) {
      
        fileName = StringUtils.isBlank(fileName) ? "student-system-manager" : fileName;
        sheetName = StringUtils.isBlank(sheetName) ? "sheet0" : sheetName;

        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.addHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue());

        try(OutputStream os = response.getOutputStream()) {
            write(os,attrName,sheetName,data,clazz);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 接收一個excel文件,并且進行解析
     *  注意一旦傳入自定義監(jiān)聽器,則返回的list為空,數(shù)據(jù)需要在自定義監(jiān)聽器里面獲取
     * @param multipartFile excel文件
     * @param clazz 數(shù)據(jù)類型的class對象
     * @param readListener 監(jiān)聽器
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile multipartFile,Class<T> clazz,ReadListener<T> readListener) {
  
  
        try(InputStream inputStream = multipartFile.getInputStream()) {
            return read(inputStream,clazz,readListener);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    private static <T> void write(OutputStream os, Set<String> attrName, String sheetName, List<T> data, Class<T> clazz) {
        ExcelWriterBuilder write = EasyExcel.write(os, clazz);
        // 如果沒有指定要寫出那些屬性數(shù)據(jù),則寫出全部
        if (!CollectionUtils.isEmpty(attrName)) {
            write.includeColumnFiledNames(attrName);
        }
        write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(data);
    }


    private static <T> List<T> read(InputStream in,Class<T> clazz, ReadListener<T> readListener) {
        List<T> list = new ArrayList<>();

        Optional<ReadListener> optional = Optional.ofNullable(readListener);

        EasyExcel.read(in, clazz, optional.orElse(new AnalysisEventListener<T>() {

            @Override
            public void invoke(T data, AnalysisContext context) {
                list.add(data);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                  System.out.println("解析完成");
            }
        })).sheet().doRead();

        return list;
    }


}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容