利用poi写一个工具类导出逐级合并的单元格的Excel(通用)

java 复制代码
package ddd.special.infrastructure.utils;

import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;

import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;


/**
 * 完整版导出excel,行和列,分级别表头
 */
public class EUtils {

    //记录起始行数
    static int a =0;
    //记录列坐标
    static int rank =0;
    //用来计算  该单元格的高度n行
    static int total=0;
    //记录row行坐标,因为该api生成表格原则是逐行row生成
    static Set<Integer> set=new HashSet<>();
    //记录每个单元格个坐标
    static List<Object> list =new ArrayList<>();
    //记录合并单元格,集体合并,并生成样式,解决合并后样式问题
    static List<CellRangeAddress> listRegion =new ArrayList<>();


    public static void main(String[] args) throws IOException {
        List<Map<String,Object>> menuList=new ArrayList<>();
        Map<String,Object> map1=new HashMap<>();
        map1.put("text","1单元");
        Map<String,Object> map2=new HashMap<>();
        map2.put("text","2单元");
        Map<String,Object> map3=new HashMap<>();
        map3.put("text","3单元");
        List<Map<String,Object>> menuList2=new ArrayList<>();
        menuList2.add(map1);
        menuList2.add(map2);
        menuList2.add(map3);

        Map<String,Object> map=new HashMap<>();
        map.put("children",menuList2);
        map.put("text","李二");
        menuList.add(map);
        System.out.println(menuList);

        Map<String,String> header = new LinkedHashMap<>();
        header.put("a","标题1");
        header.put("b","标题2");
        header.put("c","标题3");
        header.put("d","标题4");
        header.put("e","标题5");
        List<Map<String,Object>> body  = new ArrayList<>();
        Map<String,Object> map4=new HashMap<>();
        map4.put("a","1");
        map4.put("b","2");
        map4.put("c","3");
        map4.put("d","4");
        map4.put("e","5");
        body.add(map4);
        Thread thread1 = new Thread(()->excelLocal(menuList,header,body,"qqq1"));
        Thread thread2 = new Thread(()->excelLocal(menuList,header,body,"qqq2"));
        thread1.start();
        thread2.start();
        try {
            //两个线程必须都执行完毕后
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("两个线程都已经执行完毕");
    }

    //本地测试下载
    @SneakyThrows
    public static synchronized void excelLocal(List<Map<String,Object>> menuList,Map<String,String> header, List<Map<String,Object>> body,String fileName) {
        System.out.println("线程名:" + Thread.currentThread().getName() + ",运行开始");
        Thread.sleep(2000);
        XSSFWorkbook wb = handlerContext(menuList,header,body,fileName);
        OutputStream out= new FileOutputStream("E:\\"+fileName+".xlsx");
        wb.write(out);
        out.flush();
        System.out.println("线程名:" + Thread.currentThread().getName() + ",运行结束");
    }
    //暴露对外端口,实现响应到客户端下载
    public static void excelExport(List<Map<String,Object>> menuList,Map<String,String> header, List<Map<String,Object>> body,String fileName, HttpServletResponse response) throws IOException {
        XSSFWorkbook wb = handlerContext(menuList,header,body,fileName);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.addHeader("Content-Disposition", "attachment; filename=" + new String((fileName+".xlsx").getBytes("UTF-8"), "ISO8859-1"));
        wb.write(response.getOutputStream());
    }


    public static XSSFWorkbook handlerContext(List<Map<String,Object>> menuList,Map<String,String> header, List<Map<String,Object>> body,String fileName){
        //创建HSSFWorkbook对象----------表格文档对象
        XSSFWorkbook wb = new XSSFWorkbook();
        //创建HSSFSheet对象----------------创建表单对象
        XSSFSheet sheet = wb.createSheet(fileName);
        return  count(menuList,setContent(wb,sheet,header,body,fileName),sheet);

    }


    private static XSSFWorkbook  count(List<Map<String,Object>> menuList,XSSFWorkbook wb,XSSFSheet sheet)  {

        try {
            XSSFRow row = null;
            //起始行+2
            a=2;
            //进行树状数据运算,计算下标
            for (Map<String,Object> menu: menuList) {
                toData(menu);
                rank=0;
            }
            //生成行标题单元格
            Iterator<Integer> iterator = set.iterator();
            while (iterator.hasNext()){
                int with=iterator.next();
                row = sheet.getRow(with)==null?sheet.createRow(with):sheet.getRow(with);
                row.setHeight((short) (30 * 20));
                for (Object l : list) {
                    List value = (ArrayList)l;
                    Object o = value.get(0);
                    Object o1 = value.get(1);
                    Object o2 = value.get(2);
                    if (with==(int)o){
                        XSSFCell cell = row.createCell((int)o1);
                        cell.setCellValue((String) o2);
                        cell.setCellStyle(style2(wb));
                    }
                }
            }
            //合并单元格
            for (CellRangeAddress region : listRegion ) {
                sheet.addMergedRegion(region);
            }
            return wb;
        }catch (Throwable e){
            e.printStackTrace();
            return null;
        }finally {
            //记录起始行数
            a =0;
            //记录列坐标
            rank =0;
            //用来计算  该单元格的高度n行
            total=0;
            //记录row行坐标,因为该api生成表格原则是逐行row生成
            set=new HashSet<>();
            //记录每个单元格个坐标
            list =new ArrayList<>();
            //记录合并单元格,集体合并,并生成样式,解决合并后样式问题
            listRegion =new ArrayList<>();
        }
    }

    //计算每个单元格坐标

    private static void toData(Map<String,Object> m)  {

        total=0;
        toTotal(m);
        int i=total==0?1:total;
        int with=i-1+a;
        if(a!=with) {
            CellRangeAddress region = new CellRangeAddress(a, with, rank, rank);
            listRegion.add(region);
        }

        List<Object> list1 =new ArrayList<>();
        list1.add(a);
        list1.add(rank);
        list1.add(m.get("text"));
        list1.add(with);
        list.add(list1);
        set.add(a);
        rank++;

        List<Map<String,Object>> children = (List<Map<String, Object>>) m.get("children");
        if (children==null||children.size()==0){
            a=a+total;
        }

        if (children!=null&&children.size()!=0){
            for (Map<String,Object> menu: children ) {
                toData(menu);
                rank--;
            }
        }
    }
    //计算当前单元格高度为n行

    private static void toTotal(Map<String,Object> m) {
        List<Map<String,Object>> children = (List<Map<String,Object>>)m.get("children");
        if (children==null||children.size()==0){
            total++;
            return;
        }
        children.stream().forEach(EUtils::toTotal);
    }
    //样式1标题行格式
    private static XSSFCellStyle style1(XSSFWorkbook wb){
        // 设置这些样式
        XSSFCellStyle style = wb.createCellStyle();
//        style.setBorderBottom(HSSFCellStyle.BORDER_THIN); //下边框
//        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);//左边框
//        style.setBorderTop(HSSFCellStyle.BORDER_THIN);//上边框
//        style.setBorderRight(HSSFCellStyle.BORDER_THIN);//右边框
//        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直bai
//        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平du
        style.setWrapText(true);//设置自动换行
        XSSFFont font = wb.createFont();
        font.setBold(true);
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 13);
        style.setFont(font);
        return style;
    }
    //样式2身体格式

    private static XSSFCellStyle style2(XSSFWorkbook wb){
        XSSFCellStyle style = wb.createCellStyle();
//        style.setBorderBottom(HSSFCellStyle.BORDER_THIN); //下边框
//        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);//左边框
//        style.setBorderTop(HSSFCellStyle.BORDER_THIN);//上边框
//        style.setBorderRight(HSSFCellStyle.BORDER_THIN);//右边框
        style.setVerticalAlignment(VerticalAlignment.CENTER);//垂直bai
//        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平du
//        style.setWrapText(true);//设置自动换行
        // 生成一个字体
        XSSFFont font = wb.createFont();
//        font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//加粗
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 11);
        // 把字体应用到当前的样式
        style.setFont(font);
        return style;
    }



    private static XSSFWorkbook setContent( XSSFWorkbook wb,  XSSFSheet sheet ,Map<String,String> header, List<Map<String,Object>> body, String fileName) {
        //创建HSSFSheet对象----------------创建表单对象
        sheet.setDefaultColumnWidth(20);
        // 产生表格抬头行
        XSSFCell cellHeader;
        XSSFRow row = sheet.createRow(0);
        cellHeader = row.createCell(0);
        row.setHeight((short) (30 * 12));
        cellHeader.setCellStyle(style1(wb));
        cellHeader.setCellValue(fileName);

        //合并
        sheet.addMergedRegion(new CellRangeAddress(0,0,0,header.size()-1));
        //生成列标题
        row = sheet.createRow(1);
        int j =0;
        for (String key :header.keySet()) {
            cellHeader = row.createCell(j);
            row.setHeight((short) (30 * 20));
            cellHeader.setCellStyle(style1(wb));
            cellHeader.setCellValue(header.get(key));
            //自适应行宽
            sheet.autoSizeColumn(j);
            sheet.setColumnWidth(j, sheet.getColumnWidth(j) * 17 / 10);
            j++;
        }

        //生成身体数据
        for (int x = 0; x < body.size(); x++) {
            Map<String, Object> data = body.get(x);
            row = sheet.createRow(x+2);
            for (int i=0;i<header.keySet().size();i++) {
                cellHeader = row.createCell(i);
                row.setHeight((short) (30 * 20));
                cellHeader.setCellValue((String) data.get(header.get(i)));
            }
            int y = 0;
            for (String key :header.keySet()) {
                cellHeader = row.createCell(y);
                row.setHeight((short) (30 * 20));
                cellHeader.setCellValue((String) data.get(key));
                y++;
            }

        }
        return wb;
    }

}

运行控制台:

运行结果

相关推荐
陌上花开࿈2 小时前
调用第三方接口
java
Aileen_0v02 小时前
【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】
android·java·人工智能·云计算·ocr·腾讯云·玩转腾讯云ocr
桂月二二4 小时前
Java与容器化:如何使用Docker和Kubernetes优化Java应用的部署
java·docker·kubernetes
liuxin334455664 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
大G哥4 小时前
pytest自动化测试数据驱动yaml/excel/csv/json
json·excel·pytest
小马爱打代码4 小时前
设计模式详解(建造者模式)
java·设计模式·建造者模式
小奥超人4 小时前
Excel粘贴复制不完整的原因以及解决方法
windows·经验分享·microsoft·excel·办公技巧
code04号4 小时前
python脚本:批量提取excel数据
开发语言·python·excel
栗子~~5 小时前
idea 8年使用整理
java·ide·intellij-idea
2301_801483695 小时前
Maven核心概念
java·maven