Java对poi表格导出的代码封装

前言

本文展示的代码主要是对poi表格(xls)导出的一些封装,减少重复代码块,类封装了常用的导出形式(列-内容),支持常用的表格汇总公式,如合计,平均,累加,相除等。

效果图如下

image-20230924201807121

主要依赖

      <!--核心依赖-->	  
	  <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.0.1</version>
      </dependency>

	  <!--可有可无,可替换为其他-->
      <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.11</version>
      </dependency>

代码

StrapExcel

package com.strap.common.office;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

/**
 * <p>表格workbook定义</p>
 *
 * @author strap
 */
public class StrapExcel {

    private final HSSFWorkbook workbook;

    private HSSFCellStyle headerStyle;

    private HSSFCellStyle thStyle;

    private HSSFCellStyle cellStyle;

    private Map<String, HSSFCellStyle> formulaRecord;

    public StrapExcel() {
        this.workbook = new HSSFWorkbook();
    }

    public HSSFWorkbook getWorkbook() {
        return workbook;
    }

    public HSSFCellStyle cellStyle(Consumer<HSSFCellStyle> styleDeal, Consumer<HSSFFont> fontDeal) {
        HSSFCellStyle style = workbook.createCellStyle();
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setLocked(false);
        if (styleDeal == null) {
            // 自定义样式,居中
            style.setWrapText(true);
            style.setAlignment(HorizontalAlignment.CENTER);
            style.setVerticalAlignment(VerticalAlignment.CENTER);
        } else {
            styleDeal.accept(style);
        }
        HSSFFont font = workbook.createFont();
        // 设置头部粗体
        font.setFontName("仿宋_GB2312");
        if (fontDeal != null) {
            fontDeal.accept(font);
        }
        style.setFont(font);
        return style;
    }

    public HSSFCellStyle getHeaderStyle() {
        if (headerStyle == null) {
            headerStyle = cellStyle(null, font -> {
                font.setBold(true);
                font.setFontHeightInPoints((short) 15);
            });
        }
        return headerStyle;
    }

    public HSSFCellStyle getThStyle() {
        if (thStyle == null) {
            thStyle = cellStyle(null, font -> {
                font.setBold(true);
                font.setFontHeightInPoints((short) 12);
            });
        }
        return thStyle;
    }

    public HSSFCellStyle getCellStyle() {
        if (cellStyle == null) {
            cellStyle = cellStyle(null, font -> font.setFontHeightInPoints((short) 12));
        }
        return cellStyle;
    }

    public HSSFCellStyle getFormulaStyle(String format) {
        String key = CharSequenceUtil.nullToEmpty(format);
        if (formulaRecord == null) {
            formulaRecord = new HashMap<>();
        }
        return formulaRecord.computeIfAbsent(key, k -> cellStyle(style -> {
            style.setWrapText(true);
            style.setLocked(true);
            style.setAlignment(HorizontalAlignment.CENTER);
            style.setVerticalAlignment(VerticalAlignment.CENTER);
            if (StrUtil.isNotEmpty(format)) {
                short formatIdx = HSSFDataFormat.getBuiltinFormat(format);
                style.setDataFormat(formatIdx > -1 ? formatIdx : workbook.createDataFormat().getFormat(format));
            }
        }, font -> font.setFontHeightInPoints((short) 12)));
    }

    public void createSheet(StrapSheet strapSheet) throws Exception {
        strapSheet.createSheet(this);
        strapSheet.renderHeaderTitle();
        strapSheet.renderColList();
        strapSheet.renderCells();
        strapSheet.renderFormula();
    }

    public byte[] getBytes() throws Exception {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            workbook.write(bos);
            return bos.toByteArray();
        }
    }

    public void saveFile(String filePath) throws Exception {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            workbook.write(fos);
        }
    }

}

StrapSheet

package com.strap.common.office;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>表格worksheet定义</p>
 *
 * @author strap
 */
public class StrapSheet {

    private static final String DEFAULT_SHEET_NAME = "Sheet";

    private static final String DEFAULT_TOTAL_NAME = "合计";

    private String sheetName;

    private String headerTitle;

    private List<StrapCol> colList;

    private List<List<Object>> cells;

    private int cellStart = -1;

    private HSSFSheet sheet;

    private StrapExcel strapExcel;

    public StrapSheet() {
        this.colList = new ArrayList<>();
        this.cells = new ArrayList<>();
    }

    public String getSheetName() {
        return sheetName;
    }

    public StrapSheet sheetName(String sheetName) {
        this.sheetName = sheetName;
        return this;
    }

    public String getHeaderTitle() {
        return headerTitle;
    }

    public StrapSheet headerTitle(String headerTitle) {
        this.headerTitle = headerTitle;
        return this;
    }

    public List<StrapCol> getColList() {
        return colList;
    }

    public StrapSheet initColList(List<StrapCol> colList) {
        this.colList = colList;
        return this;
    }

    public StrapSheet addColList(String... cols) {
        if (ArrayUtil.isNotEmpty(cols)) {
            Arrays.stream(cols).forEach(s -> addColList(new StrapCol(s)));
        }
        return this;
    }

    public StrapSheet addColList(StrapCol... cols) {
        if (ArrayUtil.isNotEmpty(cols)) {
            Arrays.stream(cols).forEach(this::addColList);
        }
        return this;
    }

    public StrapSheet addColList(List<StrapCol> cols) {
        if (ArrayUtil.isNotEmpty(cols)) {
            cols.forEach(this::addColList);
        }
        return this;
    }

    public int addColList(StrapCol col) {
        int index = CollectionUtil.indexOf(this.colList, c -> c.getColName().equals(col.getColName()));
        if (index == -1) {
            this.colList.add(col);
            index = this.colList.size() - 1;
        }
        return index;
    }

    public List<List<Object>> getCells() {
        return cells;
    }

    public StrapSheet addValuesList(List<List<Object>> values) {
        if (CollectionUtil.isNotEmpty(values)) {
            this.cells.addAll(values);
        }
        return this;
    }

    public StrapSheet addValues(Object... values) {
        if (ArrayUtil.isNotEmpty(values)) {
            this.cells.add(Arrays.stream(values).collect(Collectors.toList()));
        }
        return this;
    }

    public StrapSheet addValuesMap(List<Map<String, Object>> values) {
        if (CollectionUtil.isNotEmpty(values)) {
            for (Map<String, Object> val : values) {
                Map<Integer, Object> record = new HashMap<>();
                for (Map.Entry<String, Object> entry : val.entrySet()) {
                    int index = addColList(new StrapCol(entry.getKey()));
                    record.put(index, entry.getValue());
                }
                int maxSize = record.keySet().stream().max(Integer::compareTo).orElse(0) + 1;
                Object[] targetValues = new Object[maxSize];
                for (Map.Entry<Integer, Object> entry : record.entrySet()) {
                    targetValues[entry.getKey()] = entry.getValue();
                }
                addValues(targetValues);
            }

        }
        return this;
    }

    public HSSFSheet getSheet() {
        return sheet;
    }

    public void createSheet(StrapExcel strapExcel) throws Exception {
        if (StrUtil.isEmpty(sheetName)) {
            sheetName = DEFAULT_SHEET_NAME;
        } else if (sheetName.length() > 27) {
            // 名称最长31
            sheetName = sheetName.substring(0, 27);
        }
        this.strapExcel = strapExcel;
        HSSFWorkbook workbook = strapExcel.getWorkbook();
        int sheetIndex = workbook.getSheetIndex(sheetName);
        int i = 0;
        while (sheetIndex > -1) {
            sheetIndex = workbook.getSheetIndex(sheetName + ++i);
        }
        this.sheet = workbook.createSheet(i > 0 ? sheetName + i : sheetName);
    }

    public int nextRowIndex() {
        int lastRowNum = sheet.getLastRowNum();
        if (lastRowNum == 0) {
            return sheet.getRow(lastRowNum) == null ? 0 : 1;
        }
        return lastRowNum + 1;
    }

    public void fillValue(HSSFCell cell, Object value) {
        String s = StrUtil.toStringOrNull(value);
        if (CharSequenceUtil.isEmpty(s)) {
            cell.setCellValue("");
        } else if (value instanceof Number || NumberUtil.isNumber(s)) {
            cell.setCellValue(Double.parseDouble(s));
        } else {
            cell.setCellValue(s);
        }
    }

    public void initMergeCell(int firstRow, int lastRow, int firstCol, int lastCol) {
        CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
        sheet.addMergedRegion(cellRangeAddress);
        RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, sheet);
        RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, sheet);
        RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, sheet);
        RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, sheet);
    }

    private int getColSize() {
        return CollectionUtil.isEmpty(colList) ? CollectionUtil.size(IterUtil.getFirst(cells)) : colList.size();
    }

    public void renderHeaderTitle() {
        if (CharSequenceUtil.isNotEmpty(headerTitle)) {
            int index = nextRowIndex();
            HSSFRow row = sheet.createRow(index);
            row.setHeight((short) (450));
            HSSFCell cell = row.createCell(0);
            cell.setCellValue(headerTitle);
            cell.setCellStyle(strapExcel.getHeaderStyle());
            initMergeCell(index, index, 0, getColSize() - 1);
        }
    }

    public void renderColList() {
        if (CollectionUtil.isNotEmpty(getColList())) {
            HSSFRow thRow = sheet.createRow(nextRowIndex());
            thRow.setHeight((short) 500);
            for (int i = 0; i < getColList().size(); i++) {
                HSSFCell cell = thRow.createCell(i);
                StrapCol strapCol = getColList().get(i);
                cell.setCellValue(strapCol.getColName());
                cell.setCellStyle(strapExcel.getThStyle());
                sheet.setColumnWidth(i, strapCol.getWidth());
            }
        }
    }

    public void renderCells() {
        if (CollectionUtil.isNotEmpty(cells)) {
            int currentRowIndex = nextRowIndex();
            cellStart = currentRowIndex;
            for (List<Object> innerCells : cells) {
                HSSFRow row = sheet.createRow(currentRowIndex++);
                for (int i = 0; i < innerCells.size(); i++) {
                    HSSFCell cell = row.createCell(i);
                    cell.setCellStyle(strapExcel.getCellStyle());
                    fillValue(cell, innerCells.get(i));
                }
                int colSize = getColSize();
                if (colSize > innerCells.size()) {
                    // 空位置补充画方格
                    for (int i = innerCells.size(); i < colSize; i++) {
                        HSSFCell cell = row.createCell(i);
                        cell.setCellStyle(strapExcel.getCellStyle());
                    }
                }
            }
        }
    }

    public void renderFormula() throws Exception {
        if (cellStart > -1 && CollectionUtil.isNotEmpty(colList) && colList.stream().anyMatch(StrapCol::isFormula)) {
            HSSFRow row = sheet.createRow(nextRowIndex());
            for (int i = 0; i < colList.size(); i++) {
                HSSFCell cell = row.createCell(i);
                StrapCol strapCol = colList.get(i);
                String formula = Optional.ofNullable(strapCol.getFormula()).map(
                        f -> f.type(strapCol, colList, cellStart, cells.size())
                ).orElse(null);
                if (StrUtil.isEmpty(formula)) {
                    fillValue(cell, i == 0 && CharSequenceUtil.isEmpty(strapCol.getCustomVal()) ? DEFAULT_TOTAL_NAME : strapCol.getCustomVal());
                    cell.setCellStyle(CharSequenceUtil.isEmpty(strapCol.getFormat()) ? strapExcel.getCellStyle() : strapExcel.getFormulaStyle(strapCol.getFormat()));
                } else {
                    cell.setCellFormula(formula);
                    cell.setCellStyle(strapExcel.getFormulaStyle(strapCol.getFormat()));
                }
            }
        }
    }

}

StrapCol

package com.strap.common.office;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.poi.ss.util.CellReference;

import java.util.List;

/**
 * <p>表格列定义</p>
 *
 * @author strap
 */
public class StrapCol {

    /**
     * 列名
     */
    private final String colName;

    /**
     * 头部列名宽度
     */
    private final Integer width;

    /**
     * 合计方式
     */
    private Formula formula;

    /**
     * 合计时使用的自定义值
     */
    private String customVal;

    /**
     * 合计时分子列
     */
    private String numerator;

    /**
     * 合计时分母列
     */
    private String denominator;

    /**
     * 自定义合计格式
     */
    private String format;

    public StrapCol(String colName) {
        this.colName = colName;
        this.width = 700 + colName.length() * 600;
    }

    public StrapCol(String colName, Integer width) {
        this.colName = colName;
        this.width = width;
    }

    public Formula getFormula() {
        return formula;
    }

    public StrapCol setFormula(Formula formula) {
        this.formula = formula;
        return this;
    }

    public String getFormat() {
        return format;
    }

    public StrapCol setFormat(String format) {
        this.format = format;
        return this;
    }

    public String getColName() {
        return colName;
    }

    public Integer getWidth() {
        return width;
    }

    public StrapCol setCustomVal(String customVal) {
        this.customVal = customVal;
        return this;
    }

    public StrapCol setDivide(String numerator, String denominator, String format) {
        this.numerator = numerator;
        this.denominator = denominator;
        this.format = format;
        this.formula = Formula.Divide;
        return this;
    }

    public String getCustomVal() {
        return customVal;
    }

    public String getNumerator() {
        return numerator;
    }

    public String getDenominator() {
        return denominator;
    }

    public boolean isFormula() {
        return getFormula() != null || CharSequenceUtil.isNotEmpty(customVal);
    }

    public enum Formula {
        SUM {
            @Override
            public String type(StrapCol strapCol, List<StrapCol> colList, int start, int cellSize) {
                int i = CollectionUtil.indexOf(colList, col -> strapCol.getColName().equals(col.getColName()));
                if (i > -1) {
                    String s = CellReference.convertNumToColString(i);
                    return StrUtil.format("SUM({}{}:{}{})", s, start + 1, s, start + cellSize);
                }
                return "";
            }
        },

        COUNT {
            @Override
            public String type(StrapCol strapCol, List<StrapCol> colList, int start, int cellSize) {
                int i = CollectionUtil.indexOf(colList, col -> strapCol.getColName().equals(col.getColName()));
                if (i > -1) {
                    String s = CellReference.convertNumToColString(i);
                    return StrUtil.format("COUNTA({}{}:{}{})", s, start + 1, s, start + cellSize);
                }
                return "";
            }
        },

        AVERAGE {
            @Override
            public String type(StrapCol strapCol, List<StrapCol> colList, int start, int cellSize) {
                int i = CollectionUtil.indexOf(colList, col -> strapCol.getColName().equals(col.getColName()));
                if (i > -1) {
                    String s = CellReference.convertNumToColString(i);
                    return StrUtil.format("SUM({}{}:{}{})/{}", s, start + 1, s, start + cellSize, cellSize);
                }
                return "";
            }
        },

        Divide {
            @Override
            public String type(StrapCol strapCol, List<StrapCol> colList, int start, int cellSize) {
                int x = CollectionUtil.indexOf(colList, col -> strapCol.getNumerator().equals(col.getColName()));
                int y = CollectionUtil.indexOf(colList, col -> strapCol.getDenominator().equals(col.getColName()));
                if (x > -1 && y > -1) {
                    String xStr = CellReference.convertNumToColString(x);
                    String yStr = CellReference.convertNumToColString(y);
                    int rowIndex = start + cellSize + 1;
                    return StrUtil.format("IF({}{}=0, \"-\", {}{}/{}{})", yStr, rowIndex, xStr, rowIndex, yStr, rowIndex);
                }
                return "";
            }
        };

        public abstract String type(StrapCol strapCol, List<StrapCol> colList, int start, int cellSize);
    }

}

Demo

package com.strap.test.office;


import cn.hutool.core.map.MapUtil;
import com.strap.common.office.StrapCol;
import com.strap.common.office.StrapExcel;
import com.strap.common.office.StrapSheet;
import org.junit.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

public class TestExport {


    @Test
    public void exportXls() throws Exception {

        List<StrapCol> strapCols = Arrays.asList(
                new StrapCol("班级", 5000), // 自定义列宽
                new StrapCol("姓名").setFormula(StrapCol.Formula.COUNT), // 统计行
                new StrapCol("年龄").setFormula(StrapCol.Formula.AVERAGE).setFormat("0.0"), // 列平均,保留一位小数
                new StrapCol("分数").setFormula(StrapCol.Formula.SUM), // 列累加
                new StrapCol("平均分").setDivide("分数", "姓名", "0.0"), // 不同汇总列相除
                new StrapCol("评级", 5000).setCustomVal("这是自定义值") // 自定义汇总值
        );
        StrapExcel strapExcel = new StrapExcel();

        // 创建第一个sheet
        strapExcel.createSheet(
                new StrapSheet().addColList(strapCols)
                        .sheetName("一年(1)班")   // 表名
                        .headerTitle("一年(1)班成绩统计") // 表头名
                        .addValuesMap(
                                Arrays.asList(
                                        MapUtil.builder(new HashMap<String, Object>()).put("班级", "一年(1)班")
                                                .put("姓名", "林一").put("评级", "B")
                                                .put("年龄", 8).put("平均分", 55).put("分数", 55.6).build(),
                                        MapUtil.builder(new HashMap<String, Object>()).put("班级", "一年(1)班")
                                                .put("姓名", "徐二").put("评级", "C")
                                                .put("年龄", 7).put("平均分", 66).put("分数", 55.6).build()
                                )
                        ).addValuesList(
                                Arrays.asList(
                                        Arrays.asList("一年(1)班", "张三", 7, 75.5, 65, "A"),
                                        Arrays.asList("一年(1)班", "李四", 6, 63.5, 65, "C"),
                                        Arrays.asList("一年(1)班", "陈五", 8, 62.5, 65, "B"),
                                        Arrays.asList("一年(1)班", "王六", 7, 80.5, 65, "A")
                                )
                        )
        );

        // 创建第二个sheet
        strapExcel.createSheet(
                new StrapSheet().addColList(strapCols)
                        .sheetName("一年(2)班")
                        .headerTitle("一年(2)班成绩统计")
                        .addValuesMap(
                                Arrays.asList(
                                        MapUtil.builder(new HashMap<String, Object>()).put("班级", "一年(2)班")
                                                .put("姓名", "林一").put("评级", "B")
                                                .put("年龄", 8).put("平均分", 55).put("分数", 55.6).build(),
                                        MapUtil.builder(new HashMap<String, Object>()).put("班级", "一年(2)班")
                                                .put("姓名", "徐二").put("评级", "C")
                                                .put("年龄", 7).put("平均分", 66).put("分数", 55.6).build()
                                )
                        ).addValuesList(
                                Arrays.asList(
                                        Arrays.asList("一年(2)班", "张三", 7, 75.5, 65, "A"),
                                        Arrays.asList("一年(2)班", "李四", 6, 63.5, 65, "C"),
                                        Arrays.asList("一年(2)班", "陈五", 8, 62.5, 65, "B"),
                                        Arrays.asList("一年(2)班", "王六", 7, 80.5, 65, "A")
                                )
                        )
        );

        // 创建第三个sheet
        strapExcel.createSheet(new StrapSheet().addColList(strapCols)
                .sheetName("一年(3)班")
                .headerTitle("一年(3)班成绩统计")
                .addValuesMap(
                        Arrays.asList(
                                MapUtil.builder(new HashMap<String, Object>()).put("班级", "一年(3)班")
                                        .put("姓名", "林一").put("评级", "B")
                                        .put("年龄", 8).put("平均分", 55).put("分数", 55.6).build(),
                                MapUtil.builder(new HashMap<String, Object>()).put("班级", "一年(3)班")
                                        .put("姓名", "徐二").put("评级", "C")
                                        .put("年龄", 7).put("平均分", 66).put("分数", 55.6).build()
                        )
                ).addValuesList(
                        Arrays.asList(
                                Arrays.asList("一年(3)班", "张三", 7, 75.5, 65, "A"),
                                Arrays.asList("一年(3)班", "李四", 6, 63.5, 65, "C"),
                                Arrays.asList("一年(3)班", "陈五", 8, 62.5, 65, "B"),
                                Arrays.asList("一年(3)班", "王六", 7, 80.5, 65, "A")
                        )
                )
        );


        // byte[] bytes = strapExcel.getBytes();
        strapExcel.saveFile("E:\\test.xls");
    }

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值