Hutool Java 工具類庫導出 Excel並合並數據,全網最詳細!


ps:基於HuTool工具類ExcelWriter合並單元格並且使用 jdk1.8 lambda表達式

一、原始數據模板

 

 

 

二、合並后的數據

按照班級名稱、班級分數、小組名稱、小組得分、人物名稱、人物總分進行單元格合並

合並后效果:


 

 

 

三、導入依賴
ps:pom依賴版本不合適可以換其他版本

導出是項目中最常見的功能,例如考勤記錄導出,賬單明細導出,訂單記錄導出等等。導出的工具類有許多種,目前常見的有poi,easypoi,poi...,今天我要說的是基於hutool-poi的導出,hutool-poi是將poi做了封裝,簡化了大量的代碼編寫。
使用方式:
maven
在項目的pom.xml中引入
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.5.1</version>
</dependency>
gradle
在項目的build.gradle中引入
compile 'cn.hutool:hutool-all:5.7.3'

 


四、代碼邏輯

1.查找數據庫返回數據voList;
2.設置導出表頭數據;
3.用lambda表達式獲取字段分組數據;
4.遍歷數據,設置合並規則;
5.將數據保存在list中;
6.ExcelWriter導出excel文件

 

五、代碼
1.需實現將人物信息導出到excel文件中並且還要按照班級名稱,小組名稱,人物名稱動態合並單元格,所以先創建人物對象:

@Data
@AllArgsConstructor //生成所有參數的構造器
public class Person {
    //班級名稱
    private String className;
    //班級分數
    private double classScore;
    //小組名稱
    private String groupName;
    //小組分數
    private double groupScore;
    //人物姓名
    private String personName;
    //人物總分
    private double personScore;
    //學科名稱
    private String subjectName;
    //學科分數
    private double subjectScore;
}

 


2.導出核心代碼:

@Slf4j
@RestController
@RequestMapping("/person")
public class PersonController {
    /**
     * 將頁面的數據導出並合並單元格
     * @param response
     */
    @ApiOperation(value = "導出數據到excel")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "response", value = "響應體對象", dataType = "HttpServletResponse")
    })
    @GetMapping("/export")
    public void export(HttpServletResponse response)throws Exception{
        //1.模擬一些人物對象數據(工作中從數據庫查出來)
        List<Person> list = new ArrayList<>();
        list.add(new Person("一班",90,"一組",89,"孫悟空",89,"語文",89));
        list.add(new Person("一班",90,"一組",89,"孫悟空",89,"數學",98));
        list.add(new Person("一班",90,"一組",89,"唐僧",78,"語文",98));
        list.add(new Person("一班",90,"一組",89,"唐僧",78,"數學",78));
        list.add(new Person("一班",90,"二組",90,"沙悟凈",90,"語文",90));
        list.add(new Person("一班",90,"二組",90,"沙悟凈",90,"數學",90));
        list.add(new Person("二班",91,"一組",97,"魯智深",98,"語文",89));
        list.add(new Person("二班",91,"一組",97,"魯智深",98,"數學",98));
        list.add(new Person("二班",91,"二組",89,"宋江",89,"語文",98));
        list.add(new Person("二班",91,"二組",89,"宋江",89,"數學",78));
        list.add(new Person("二班",91,"二組",89,"林沖",88,"語文",90));
        list.add(new Person("二班",91,"二組",89,"林沖",88,"數學",90));

        //2.定義基礎數據
        List<String> rowHead = CollUtil.newArrayList("班級名稱","班級分數","小組名稱","小組分數","角色姓名","角色總分","學科名稱","學科分數");
        //3.通過ExcelUtil.getBigWriter()創建Writer對象,BigExcelWriter用於大數據量的導出,不會引起溢出;
        ExcelWriter writer = ExcelUtil.getBigWriter();
        //4.寫入標題
        writer.writeHeadRow(rowHead);
        ServletOutputStream out = null;
        //5.實現核心邏輯
        try {
            //6.定義容器保存人物數據
            List<List<Object>> rows = new LinkedList<>();
            //7.按照班級進行分組
            LinkedHashMap<String, List<Person>> classList = list.stream().collect(Collectors.groupingBy(item -> item.getClassName(),
                    LinkedHashMap::new, Collectors.toList()));
            //8.定義起始行(方便分組后合並時從哪一行開始)
            //因為標題已經占了一行,所以數據從第二行開始寫(excel第一行索引為0)
            //因需要合並到人物分數單元格所以需定義如下起始坐標
            int indexClassName = 1;   //班級名稱起始行
            int indexClassScore = 1;
            int indexGroupName = 1;
            int indexGroupScore = 1;
            int indexPersonName = 1;
            int indexPersonScore = 1;
            //9.遍歷按班級名分組后的list(用entrySet效率比keySet效率高)
            for (Map.Entry<String, List<Person>> classNameListEntry : classList.entrySet()) {
                //10.獲取按照班級名分組后的集合
                List<Person> classValue = classNameListEntry.getValue();
                //11.計算此集合的長度
                int classSize = classValue.size();
                //12.如果只有一行數據不能調用merge方法合並數據,否則會報錯
                if (classSize == 1){
                    indexClassName += classSize;
                    indexClassScore += classSize;
                    indexGroupName += classSize;
                    indexGroupScore += classSize;
                    indexPersonName += classSize;
                    indexPersonScore += classSize;
                }else{
                    //13.根據班級名稱進行合並單元格
                    //合並行,第一個參數是合並行的開始行號(行號從0開始),第二個參數是合並行的結束行號,第三個參數是合並的列號開始(列號從0開始),
                    //第四個參數是合並的列號結束,第五個參數是合並后的內容,null不設置,第六個參數指是否支持設置樣式,true指的是。
                    writer.merge(indexClassName, indexClassName + classSize - 1, 0, 0, null, true);
                    //14.合並完后起始索引移到下一個合並點
                    indexClassName += classSize;
                    //15.因為班級分數與班級名稱相關聯,所以不需要對班級分數分組,直接在此基礎上對班級分數合並
                    writer.merge(indexClassScore, indexClassScore + classSize - 1, 1, 1, null, true);
                    indexClassScore += classSize;
                    //16.按照小組名進行分組(以下分組與班級名合並類似)
                    LinkedHashMap<String, List<Person>> groupList = classValue.stream().collect(Collectors.groupingBy(item -> item.getGroupName(),
                            LinkedHashMap::new, Collectors.toList()));
                    for (Map.Entry<String, List<Person>> groupListEntry : groupList.entrySet()) {
                        List<Person> groupValue = groupListEntry.getValue();
                        int groupSize = groupValue.size();
                        if (groupSize == 1){
                            indexGroupName += groupSize;
                            indexGroupScore += groupSize;
                            indexPersonName += groupSize;
                            indexPersonScore += groupSize;
                        }else{
                            //合並小組
                            writer.merge(indexGroupName, indexGroupName + groupSize - 1, 2, 2, null, true);
                            indexGroupName += groupSize;
                            //合並小組分數
                            writer.merge(indexGroupScore, indexGroupScore + groupSize - 1, 3, 3, null, true);
                            indexGroupScore += groupSize;
                            //17.按照人物名稱進行分組
                            LinkedHashMap<String, List<Person>> personList = groupValue.stream().collect(Collectors.groupingBy(item -> item.getPersonName(),
                                    LinkedHashMap::new, Collectors.toList()));
                            for (Map.Entry<String, List<Person>> personListEntry : personList.entrySet()) {
                                List<Person> personValue = personListEntry.getValue();
                                int personSize = personValue.size();
                                if (personSize == 1){
                                    indexPersonName += personSize;
                                    indexPersonScore += personSize;
                                }else{
                                    //合並人物
                                    writer.merge(indexPersonName, indexPersonName + personSize - 1, 4, 4, null, true);
                                    indexPersonName += personSize;
                                    //合並人物總分
                                    writer.merge(indexPersonScore, indexPersonScore + personSize - 1, 5, 5, null, true);
                                    indexPersonScore += personSize;
                                }
                            }
                        }
                    }

                }
                //18.保存數據
                classValue.forEach(
                        sList->{
                            List<Object> rowA = null;
                            rowA = CollUtil.newArrayList(
                                    sList.getClassName(),
                                    sList.getClassScore(),
                                    sList.getGroupName(),
                                    sList.getGroupScore(),
                                    sList.getPersonName(),
                                    sList.getPersonScore(),
                                    sList.getSubjectName(),
                                    sList.getSubjectScore()
                            );
                            rows.add(rowA);
                        }
                );
            }
            //19.導出數據
            //logger.info("導出數據:{}",rows.toString());
            // 一次性寫出內容,使用默認樣式,強制輸出標題
            writer.write(rows, true);
            //response為HttpServletResponse對象
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            //獲取當前日期作為文件名
            Date currentDate = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            String date = sdf.format(currentDate);
            //test.xls是彈出下載對話框的文件名,不能為中文,中文請自行編碼
            //response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
            response.setHeader("Content-Disposition", "attachment;filename=report_"+date+".xlsx");
            out = response.getOutputStream();
            writer.flush(out, true);
        }finally {
            //關閉輸出Servlet流
            IoUtil.close(out);
            //關閉writer,釋放內存
            writer.close();
        }
    }
}

 

六、測試

1.使用postman測試

 

 

 

2.結果:

 

 

 

 

借鑒地址:
https://juejin.cn/post/6982853657679101982

https://blog.csdn.net/zqy_CSDN_name/article/details/119643968

 


免責聲明!

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



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