EasyExcel 使用小結


  EasyExcel是一個基於Java的簡單、省內存的讀寫Excel的開源項目。在盡可能節約內存的情況下支持讀寫百M的Excel。以下是在使用 EasyExcel 的一些小結,參考內容以官方說明文檔為主,總結了使用過程中一些經驗和遇到的問題,相較於官方文檔並不全面,謹作為記錄,具體使用請參照官方使用說明。

引入EasyExcel依賴

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>${easyexcel.version}</version>
</dependency>

最新版本(2021/01/25)2.2.6  版本查看:https://github.com/alibaba/easyexcel/releases

 

EasyExcel 官方使用說明

官方的使用說明:https://www.yuque.com/easyexcel/doc/easyexcel

使用記錄

  使用方式為 Web 中的讀取,Web 中上傳文件,對數據進行處理后在 Web 中寫出。

上傳文件:

上傳多個文件

<div class="item">
    <form id="form" method="post" action="upload" enctype="multipart/form-data">
        <div class="upload-box">
            選擇文件
            <input type="file" name="uploadFile" id="fileUps" accept=".xls,.xlsx" class="upload"
                   multiple="multiple">
        </div>
        <input type="submit" value="提交" class="upload-box submit"/>
    </form>
    <div id="files-box"></div>
    <p>報價單匯總(只提交報價單)</p>
    <p>只能上傳excel格式文件!</p>
</div>

 Controller:

@PostMapping("upload")
@ResponseBody
public void upload(@RequestParam("uploadFile") MultipartFile[] uploadFileM, HttpServletResponse response) {
    mergeQuotesService.MergeQuotes(uploadFileM, response);
}

讀文件:

for (MultipartFile file : uploadFileM) {
    if (file.getOriginalFilename().contains("報價單")) {
        // 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉
        try {
            // 標題占兩行,從第三行開始讀取
            EasyExcel.read(file.getInputStream(), Quotes.class, new QuotesListener(quotesResultDto)).sheet().headRowNumber(2).doRead();
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**
         * 原始數據整理
         */
        quotesResultDto.feedBack().forEach(quotes -> removeRepeat.put(quotes.getDrawing() + quotes.getVersion(), new QuotesMergeDto(quotes)));
    }
}

 對象:

Qutote(部分):

@Data
@ContentRowHeight(12)
@HeadFontStyle(fontHeightInPoints = 10, fontName = "微軟雅黑")
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 9)
public class Quotes {

    /**
     * Module Name
     */
    @ColumnWidth(12)
    @ExcelProperty(value = {"加工件", "Module Name"}, index = 0)
    private String moduleName;


    /**
     * SWC Module
     */
    @ColumnWidth(10)
    @ExcelProperty(value = {"加工件", "SWC Module"}, index = 1)
    private String module;

    /**
     * Drawing No.
     */
    @ColumnWidth(18)
    @ExcelProperty(value = {"加工件", "Drawing No."}, index = 2)
    private String drawing;

  ... }

因為此實體類還參與了后續的寫文件,因此添加了很多自定義樣式的注解,若無此需求則不需要添加,如:

@Data
public class DemoData {
    private String string;
    private Date date;
    private Double doubleData;
}

監聽器:

 1 public class QuotesListener extends AnalysisEventListener<Quotes> {
 2 
 3 
 4     List<Quotes> list = new ArrayList<>();
 5 
 6     private Quotes quotes;
 7 
 8     @Autowired
 9     QuotesResultDAO quotesResult;
10 
11     public QuotesListener() {
12         quotes = new Quotes();
13     }
14 
15     public QuotesListener(QuotesResultDAO quotesResult) {
16         this.quotesResult = quotesResult;
17     }
18 
19     /**
20      * 這個每一條數據解析都會來調用
21      *
22      * @param quotes
23      * @param analysisContext
24      */
25     @Override
26     public void invoke(Quotes quotes, AnalysisContext analysisContext) {
27         list.add(quotes);
28     }
29 
30 
31     /**
32      * 所有數據解析完成了 才會來調用
33      *
34      * @param analysisContext
35      */
36     @Override
37     public void doAfterAllAnalysed(AnalysisContext analysisContext) {
38         quotesResult.save(list);
39     }
40 
41 }

持久層:

 1 @Repository
 2 public class QuotesResultDAO {
 3 
 4     List<Quotes> list = new ArrayList<>();
 5 
 6     /**
 7      * 存儲 Excel 中讀取的數據
 8      *
 9      * @param quotes
10      */
11     public void save(List<Quotes> quotes) {
12         list.addAll(quotes);
13     }
14 
15     /**
16      * 返回列表
17      *
18      * @return
19      */
20     public List<Quotes> feedBack() {
21         /**
22          * 在第一次返回數據時清空 list 中的內容,防止數據留存
23          */
24         List<Quotes> temp = new ArrayList<>();
25         temp.addAll(list);
26         list.clear();
27         return temp;
28     }
29 
30 }

數據處理(讀取&寫出):

 1 @Service
 2 @RequiredArgsConstructor
 3 public class MergeQuotesServiceImpl implements MergeQuotesService {
 4 
 5     private final QuotesResultDto quotesResultDto;
 6 
 7     // 報價單合並
 8     public void MergeQuotes(MultipartFile[] uploadFileM, HttpServletResponse response) {
 9 
10         // 用於對唯一ID進行去重
11         Map<String, QuotesMergeDto> removeRepeat = new HashMap<>();
12         // 最終合並結果列表
13         List<QuotesMergeDto> result = new ArrayList<>();
14 
15 
16         for (MultipartFile file : uploadFileM) {
17             if (file.getOriginalFilename().contains("報價單")) {
18                 // 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉
19                 try {
20                     // 標題占兩行,從第三行開始讀取
21                     EasyExcel.read(file.getInputStream(), Quotes.class, new QuotesListener(quotesResultDto)).sheet().headRowNumber(2).doRead();
22                 } catch (Exception e) {
23                     e.printStackTrace();
24                 }
25 
26                 /**
27                  * 原始數據整理
28                  */
29                 quotesResultDto.feedBack().forEach(quotes -> removeRepeat.put(quotes.getDrawing() + quotes.getVersion(), new QuotesMergeDto(quotes)));
30             }
31         }
32 
33         // 最終的去重數據
34         removeRepeat.forEach((k, v) -> result.add(v));
35 
36         // 報價單輸出
37         MergeResultWrite(result, response);
38     }
39 
40 
41     /**
42      * 報價單輸出
43      *
44      * @param list
45      */
46     public void MergeResultWrite(List<QuotesMergeDto> list, HttpServletResponse response) {
47 
48         // 這里注意 使用swagger 會導致各種問題,請直接用瀏覽器或者用postman
49         response.setContentType("application/vnd.ms-excel");
50         response.setCharacterEncoding("utf-8");
51         // 這里URLEncoder.encode可以防止中文亂碼 當然和easyExcel沒有關系
52         try {
53 
54             String fileName = URLEncoder.encode("匯總報價單", "UTF-8").replaceAll("\\+", "%20");
55             response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
56             // Excel 寫出合並報價單
57             EasyExcel.write(response.getOutputStream(), QuotesMergeDto.class).sheet("sheet1")
58                     .doWrite(list);
59             // System.out.println("報價單已合並");
60         } catch (Exception e) {
61             e.printStackTrace();
62         }
63 
64     }
65 
66 }

 寫文件:

@Data
@ContentRowHeight(12)
@HeadFontStyle(fontHeightInPoints = 10)
@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 9)
public class QuotesMergeDto {

    /**
     * Drawing No.
     */
    @ColumnWidth(18)
    @ExcelProperty(value = {"Drawing No."}, index = 0)
    private String drawing;

    /**
     * Version No.
     */
    @ColumnWidth(16)
    @ExcelProperty(value = {"Version No."}, index = 1)
    private String version;

    /**
     * Material
     */
    @ColumnWidth(16)
    @ExcelProperty(value = {"Material"}, index = 2)
    private String material;
    /**
     * Surface Treatment
     */
    @ColumnWidth(25)
    @ExcelProperty(value = {"Surface Treatment"}, index = 3)
    private String surface;
    /**
     * 零件類型
     */
    @ColumnWidth(15)
    @ExcelProperty(value = {"零件類型"}, index = 4)
    private String type;
}

在文件寫出時,可以按照需求設置樣式,最后傳入的 list 並不需要與實體類的類型對應,只會按照實體類的格式進行寫出,index 的值對應 列表對象中元素的位置。

 其它操作

按照序號讀取特定表單:

 1 ExcelReader excelReader = null;
 2                 try {
 3                     excelReader = EasyExcel.read(file.getInputStream()).build();
 4 
 5                     // 讀取右側第三個 sheet
 6                     ReadSheet readSheet2 =
 7                             EasyExcel.readSheet(2).head(Workmanship.class).
 8                                     registerReadListener(new SummaryListener(summaryResultDto)).headRowNumber(5).build();
 9                     excelReader.read(readSheet2);
10                 } catch (Exception e) {
11                     e.printStackTrace();
12                 } finally {
13                     if (excelReader != null) {
14                         // 這里千萬別忘記關閉,讀的時候會創建臨時文件,到時磁盤會崩的
15                         excelReader.finish();
16                     }
17                 }

使用場景:Excel 中有多個 sheet,按照需要讀取所需 sheet。

寫入多個 sheet:

 1         int number = 0; // 表單的編號 2      try { 
 3             String returnName = URLEncoder.encode("報價單+價格", "UTF-8").replaceAll("\\+", "%20");
 4             response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + returnName + ".xlsx");
 5             excelWriter = EasyExcel.write(response.getOutputStream()).build();
 6             for (MultipartFile file : files) {
 7                 if (file.getOriginalFilename().contains("報價單")) {
 8                     // 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉
 9                     try {
10                         EasyExcel.read(file.getInputStream(), Quotes.class, new QuotesListener(quotesResultDto)).sheet().headRowNumber(2).doRead();
11                     } catch (Exception e) {
12                         System.out.println(file.getOriginalFilename() + "讀取錯誤");
13                         e.printStackTrace();
14                     }
15 
16                     /**
17                      * 報價單表內數據
18                      */
19                     quoteList = quotesResultDto.feedBack();
20                     List<QuotesMergePlusPriceDto> list = new ArrayList<>();
21 
22                     quoteList.forEach(quotes -> {
23 
24                         try {
25                             // 父類賦值到子類
26                             list.add(FatherToChild.getQuotesMergePlusPriceDto(new QuotesMergePlusPriceDto(), quotes, prices.get(quotes.getDrawing() + "-" + quotes.getVersion())));
27                             28                             
29                         } catch (Exception e) {
30                             e.printStackTrace();
31                         }
32                     });
33 
34 
35                     String sheetName = file.getOriginalFilename();
36                     WriteSheet writeSheet = EasyExcel.writerSheet(number, sheetName).head(QuotesMergePlusPriceDto.class).build();
37                     number++;
38 
39                     excelWriter.write(list, writeSheet);
40 
41                 }
42 
43             }
44         } catch (Exception e) {
45             e.printStackTrace();
46 
47         } finally {
48             // 千萬別忘記finish 會幫忙關閉流
49             if (excelWriter != null) {
50                 excelWriter.finish();
51             }
52         }

編號對應顏色:

單元格填充顏色:

 

 

@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 9)

字體顏色:

@ContentFontStyle(color = 10, fontHeightInPoints = 11)

此處參考:https://my.oschina.net/u/3267498/blog/4680918


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM