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

主要依赖
<!--核心依赖-->
<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");
}
}
445

被折叠的 条评论
为什么被折叠?



