1.上一篇关于Java POI导出的文章,导出功能没有达到预期目标,有很多地方需要优化,比如,读取模板。使用上面的方式,不能获取资源文件,可使用以下方式获取。
InputStream is = this.getClass().getClassLoader().getResourceAsStream("template/" + templateName);
总的来说,抛弃了很多东西。不再使用excel模板进行导出。全过程使用代码填充。包括生成表头,聚合表头,填充数据,跨行跨列。代码下载链接,不收费https://download.csdn.net/download/ly9918/12346513
public class ExcelField {
private Integer level;
private String fieldName;
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
}
import java.util.Collection;
import java.util.List;
public class ExcelHeader {
private String index;
private String name;
private List children;
public String getIndex() {
return index;
}
public void setIndex(String index) {
this.index = index;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection> getChildren() {
return children;
}
public void setChildren(List children) {
this.children = children;
}
}
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
public class ExcelProfile {
private String excelName;
private Collection> data;
private List excelHeaders;
private Integer headerLevel;
private List excelFields;
private Integer fieldLevel;
private LinkedHashMap specialKeyMap;
public String getExcelName() {
return excelName;
}
public void setExcelName(String excelName) {
this.excelName = excelName;
}
public Collection> getData() {
return data;
}
public void setData(Collection> data) {
this.data = data;
}
public List getExcelHeaders() {
return excelHeaders;
}
public void setExcelHeaders(List excelHeaders) {
this.excelHeaders = excelHeaders;
}
public Integer getHeaderLevel() {
return headerLevel;
}
public void setHeaderLevel(Integer headerLevel) {
this.headerLevel = headerLevel;
}
public List getExcelFields() {
return excelFields;
}
public void setExcelFields(List excelFields) {
this.excelFields = excelFields;
}
public Integer getFieldLevel() {
return fieldLevel;
}
public void setFieldLevel(Integer fieldLevel) {
this.fieldLevel = fieldLevel;
}
public LinkedHashMap getSpecialKeyMap() {
return specialKeyMap;
}
public void setSpecialKeyMap(LinkedHashMap specialKeyMap) {
this.specialKeyMap = specialKeyMap;
}
}
import java.util.Collection;
import java.util.HashMap;
public class MultilevelData {
private HashMap properties;
private Collection> children;
public HashMap getProperties() {
return properties;
}
public void setProperties(HashMap properties) {
this.properties = properties;
}
public Collection> getChildren() {
return children;
}
public void setChildren(Collection> children) {
this.children = children;
}
}
3.主要用到的工具类
package com.demo.am.util;
import com.demo.am.entity.excel.ExcelField;
import com.demo.am.entity.excel.ExcelHeader;
import com.demo.am.entity.excel.ExcelProfile;
import com.demo.am.entity.excel.MultilevelData;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class ExcelExportUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportUtil.class);
// 默认排除的key
private static final String EXCLUDE_KEY = "id";
// 默认EXCEL后缀
private static final String DEFAULT_SUFFIX = ".xlsx";
// 默认编码
private static final String DEFAULT_ENCODING = "UTF-8";
// 默认时间格式
private static final String DEFAULT_DATE_PATTEN = "yyyy/MM/dd";
// 默认Row access window size
private static final Integer DATA_SIZE = 100 * 10;
// 默认字体
private static final String DEFAULT_FONT_NAME = HSSFFont.FONT_ARIAL;
// 默认EXCEL NAME所在行
private static final Integer EXCEL_NAME_ROW = 0;
// 默认起始列
private static final Integer BEGIN_COLUMN = 0;
// 默认EXCEL NAME所在行行高
private static final Integer EXCEL_NAME_ROW_HEIGHT = 20;
// 默认行高
private static final Integer DEFAULT_ROW_HEIGHT = 10;
// 最大列宽长度
private static final Integer MAX_COLUMN_WIDTH = 10;
// 填充表头完成行数
private static Integer END_HEADER_ROW = 0;
// EXCEL column number
private static Integer COLUMN_NUMBER = 0;
// 列宽集合
private static List WIDTH_DATA = new ArrayList<>();
public static void exportExcel(ExcelProfile excelProfile, HttpServletResponse response) {
exportExcel(Collections.singletonList(excelProfile), response);
}
private static void exportExcel(List excelProfileList, HttpServletResponse response) {
List errors = new ArrayList<>();
// 可以更换 XSSFWorkbook wb = new XSSFWorkbook(); 或 HSSFWorkbook wb = new HSSFWorkbook();
// 区别在于 前者为xlsx格式 后者为xls格式 , SXSSFWorkbook存在默认值100超过一百行写入磁盘文件,所以最后需要清理临时文件
XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
SXSSFWorkbook wb = new SXSSFWorkbook(xssfWorkbook, DATA_SIZE, Boolean.TRUE);
if (CollectionUtils.isNotEmpty(excelProfileList)) {
// 设置单元格样式
CellStyle style = setDefaultStyle(wb);
for (int i = 0; i < excelProfileList.size(); i++) {
ExcelProfile excelProfile = excelProfileList.get(i);
if (excelProfile != null) {
excelProfile.setExcelName(processExcelName(excelProfile.getExcelName()));
// 创建一个工作表
Sheet sheet = wb.createSheet("sheet" + i);
// 缩放比例70%
sheet.setZoom(70);
try {
fillExcelSheet(excelProfile.getExcelName(),
sheet, style,
excelProfile.getData(),
excelProfile.getExcelHeaders(),
excelProfile.getExcelFields(),
excelProfile.getSpecialKeyMap());
} catch (Exception e) {
// 捕获异常,中断循环
errors.add(e.getMessage());
break;
}
} else {
errors.add("导出失败!");
break;
}
}
} else {
errors.add("导出失败!");
}
// 导出失败存在以下两种可能
// 1.excelProfile为空
// 2.EXCEL表头没有填充
if (CollectionUtils.isNotEmpty(errors)) {
// 清理临时文件
wb.dispose();
// 关闭工作簿
closeWorkBook(wb);
// 提示消息去重复 (可能会出现重复)
List results = errors.stream().distinct().collect(Collectors.toList());
results.forEach(System.out::println);
} else {
// 处理响应头
processExcelResponse(response);
// 输出响应流
outputResponseStream(response, wb);
// 输出文件
// outputFileStream(wb);
// 清理临时文件
wb.dispose();
// 关闭工作簿
closeWorkBook(wb);
}
}
/**
* fill excel sheet.
*
* @param excelName excel name
* @param sheet excel sheet
* @param style cell style
* @param data List/List<实体类> example :List
* @param headers List/List
* @param excelFields excelFields
* @param specialKeyMap specialKeyMap
*/
private static void fillExcelSheet(String excelName, Sheet sheet, CellStyle style,
Collection> data, Collection> headers, List excelFields,
LinkedHashMap specialKeyMap) throws Exception {
// excel名称所在行 第一行
Row excelNameRow = sheet.createRow(EXCEL_NAME_ROW);
int beginRow = EXCEL_NAME_ROW + 1;
int rowNumber = beginRow;
int columnNumber = BEGIN_COLUMN;
List list = new ArrayList<>();
// 生成表头
columnNumber = fillExcelHeader(sheet, style, rowNumber, columnNumber, headers, list);
// 表头不填充,无任何响应
if (columnNumber > 0) {
// 填充表头以后,列数正确,但所占行数不正确,故使用list存放行数,获取最大值
rowNumber = Collections.max(list);
// 填充完成表头所得到row number
END_HEADER_ROW = rowNumber;
COLUMN_NUMBER = columnNumber;
// 处理excel名称所在行
processExcelNameRow(sheet, excelNameRow, style, columnNumber, excelName);
// 处理表头,包括合并、样式等等
processHeaderRow(sheet, beginRow, rowNumber, columnNumber);
// 处理表头的特殊值
processSpecialKey(sheet, specialKeyMap, rowNumber, columnNumber);
// 填充excel
fillExcelData(sheet, style, rowNumber, columnNumber, data, excelFields);
// 如果使用自动列宽需要注释掉所有 calculateWidth(),calculateHeaderColumnWidth(),setColumnWidth()
// setAutoColumnWidth(sheet,style);
} else {
throw new Exception("导出失败!");
}
}
/**
* Fill excel header.
*
* @param excelHeaders header
* @param rowNumber row
* @param columnNumber column
* @param sheet sheet
* @param style style
* @param indexList list
* @return column
*/
private static Integer fillExcelHeader(Sheet sheet, CellStyle style,
Integer rowNumber, Integer columnNumber,
Collection> excelHeaders,
List indexList) {
Row row;
indexList.add(rowNumber);
if (sheet.getRow(rowNumber) != null) {
row = sheet.getRow(rowNumber);
} else {
row = sheet.createRow(rowNumber);
}
int beginRow = rowNumber;
int beginColumn = columnNumber;
int endRow;
if (CollectionUtils.isNotEmpty(excelHeaders)) {
if (excelHeaders.toArray()[0] instanceof ExcelHeader) {
List headers = convertExcelHeaderList(excelHeaders);
for (ExcelHeader header : headers) {
fillHeaderData(row, style, Collections.singletonList(header.getName()), columnNumber);
if (CollectionUtils.isNotEmpty(header.getChildren())) {
columnNumber = fillExcelHeader(sheet, style,
rowNumber + 1, columnNumber, header.getChildren(), indexList);
} else {
columnNumber++;
}
}
} else {
List list = new ArrayList<>();
if (excelHeaders.toArray()[0] instanceof String) {
for (Object object : excelHeaders) {
list.add(String.valueOf(object));
}
}
fillHeaderData(row, style, list, columnNumber);
}
rowNumber++;
}
// 合并列
endRow = rowNumber;
if (endRow - beginRow == 1 && beginRow != 0) {
Row hssfRow = sheet.getRow(beginRow - 1);
if (columnNumber - beginColumn > 1 && hssfRow.getCell(beginColumn) != null &&
hssfRow.getCell(beginColumn + 1) == null) {
CellRangeAddress region = new CellRangeAddress(endRow - 2, endRow - 2,
beginColumn, columnNumber - 1);
sheet.addMergedRegion(region);
}
}
indexList.add(rowNumber);
return columnNumber;
}
private static void fillHeaderData(Row row, CellStyle style, List headers, Integer columnNumber) {
for (String header : headers) {
Cell cell = row.createCell(columnNumber);
setCell(cell, header, style);
columnNumber++;
}
}
private static void processExcelNameRow(Sheet sheet, Row excelNameRow, CellStyle style,
Integer columnNumber, String excelName) {
excelNameRow.setHeightInPoints(EXCEL_NAME_ROW_HEIGHT);
for (int i = 0; i < columnNumber; i++) {
Cell cell = excelNameRow.createCell(i);
cell.setCellStyle(style);
}
// excel名称所在行合并
excelNameRow.getCell(0).setCellValue(excelName);
CellRangeAddress region = new CellRangeAddress(0, 0, 0, columnNumber - 1);
sheet.addMergedRegion(region);
}
private static void processHeaderRow(Sheet sheet, Integer beginRow, Integer rowNumber, Integer columnNumber) {
Row headerRow = sheet.getRow(rowNumber - 1);
// 合并一次表头
for (int i = 0; i < columnNumber; i++) {
Cell cell = headerRow.getCell(i);
if (cell == null) {
Integer rowIndex = isExistValueBetween(sheet, beginRow, rowNumber, i);
CellRangeAddress cellAddresses;
if (rowIndex == null) {
cellAddresses = new CellRangeAddress(beginRow, rowNumber - 1, i, i);
} else {
cellAddresses = new CellRangeAddress(rowIndex, rowNumber - 1, i, i);
}
sheet.addMergedRegion(cellAddresses);
}
}
// setRowHeight(sheet, beginRow, rowNumber);
}
private static Integer isExistValueBetween(Sheet sheet,
int beginRow, int endRow, int columnNumber) {
if (endRow > beginRow) {
for (int j = beginRow; j < endRow; j++) {
Row hssfRow = sheet.getRow(j);
Cell hssfCell = hssfRow.getCell(columnNumber);
if (j > beginRow && hssfCell != null) {
return j;
}
}
}
return null;
}
/**
* Process special key.
*
* @param sheet sheet
* @param specialKeyMap special key
* @param rowNumber row number
* @param columnNumber column number
*/
private static void processSpecialKey(Sheet sheet, LinkedHashMap specialKeyMap,
Integer rowNumber, Integer columnNumber) {
// 处理表头中的特殊键
if (specialKeyMap != null && specialKeyMap.size() > 0) {
for (int i = 0; i < rowNumber; i++) {
Row hssfRow = sheet.getRow(i);
for (int j = 0; j < columnNumber; j++) {
Cell hssfCell = hssfRow.getCell(j);
if (hssfCell != null) {
String cellValue = hssfCell.getStringCellValue();
if (StringUtils.isNotBlank(cellValue)) {
specialKeyMap.forEach((key, value) -> {
if (hssfCell.getStringCellValue().contains(key)) {
// 只替换key 例如: lastYear XXX 只替换 lastYear
hssfCell.setCellValue(cellValue.replace(key, value));
}
});
}
}
}
}
}
}
/**
* Fill excel data and set column style and width.
*
* @param sheet sheet
* @param style style
* @param rowNumber begin row number
* @param columnNumber column number
* @param data data
* @param excelFields fields
*/
private static void fillExcelData(Sheet sheet, CellStyle style,
Integer rowNumber, Integer columnNumber,
Collection> data, List excelFields) {
int beginRow = rowNumber;
WIDTH_DATA = calculateHeaderColumnWidth(sheet, beginRow, columnNumber);
fillExcelData(sheet, style, rowNumber, 0, data, excelFields, 0);
setColumnWidth(sheet, style);
WIDTH_DATA.clear();
}
/**
* Fill excel data.
*
* @param data object List
* @param sheet sheet
* @param style style
* @param rowNumber row
* @param columnNumber column
* @param level level
* @param excelFields excelFields
* @return row number
*/
private static Integer fillExcelData(Sheet sheet, CellStyle style,
Integer rowNumber, Integer columnNumber,
Collection> data, List excelFields, Integer level) {
level++;
Row row;
List fields = getFields(level, excelFields);
Integer beginColumn = getBeginColumn(level, excelFields);
if (CollectionUtils.isNotEmpty(data)) {
if (data.toArray()[0] instanceof MultilevelData) {
List list = convertMultilevelDataList(data);
for (int i = 0; i < list.size(); i++) {
int beginRow = rowNumber;
MultilevelData structure = list.get(i);
if (sheet.getRow(beginRow) != null) {
row = sheet.getRow(beginRow);
} else {
row = sheet.createRow(beginRow);
}
if (i == 0) {
columnNumber = fillRowData(row, style, structure, fields, beginColumn);
} else {
fillRowData(row, style, structure, fields, beginColumn);
}
if (CollectionUtils.isNotEmpty(structure.getChildren())) {
rowNumber = fillExcelData(sheet, style,
beginRow, columnNumber, structure.getChildren(), excelFields, level);
} else {
// setRowHeight(sheet, rowNumber, rowNumber + 1);
calculateWidth(sheet, rowNumber, rowNumber + 1);
rowNumber++;
}
// 合并列
int endRow = rowNumber;
int endColumn = columnNumber;
if (endRow != 0 && endRow - beginRow > 1) {
for (int k = beginColumn; k < endColumn; k++) {
CellRangeAddress thirdRegion = new CellRangeAddress(beginRow, endRow - 1, k, k);
sheet.addMergedRegion(thirdRegion);
}
}
}
} else {
List
4.使用示例
public static void main(String[] args) {
List list = new ArrayList<>();
Collection> data = convertMultilevelDataStructure(list);
LinkedHashMap specialKeyMap = new LinkedHashMap<>();
String excelName = "角色信息表";
String suffix = ".xls";
LinkedHashMap firstHeader = new LinkedHashMap<>();
LinkedHashMap secondHeader = new LinkedHashMap<>();
LinkedHashMap thirdHeader = new LinkedHashMap<>();
LinkedHashMap> headerMap = new LinkedHashMap<>();
// 代码会将此处的value值填充到表头
firstHeader.put("1", "名称");
firstHeader.put("2", "描述");
firstHeader.put("3", "时间");
// 假设表头时间下存在创建时间和更新时间
secondHeader.put("3_1", "创建时间");
secondHeader.put("3_2", "更新时间");
// 假设创建时间下还有备注
thirdHeader.put("3_1_1", "备注");
// 然后统一放入headerMap 从1开始代表层数从1开始
headerMap.put(1, firstHeader);
headerMap.put(2, secondHeader);
headerMap.put(3, thirdHeader);
LinkedHashMap firstField = new LinkedHashMap<>();
LinkedHashMap> fieldMap = new LinkedHashMap<>();
firstField.put("name", "此处的value值不重要,key重要");
firstField.put("description", "此处的value值不重要,key重要");
firstField.put("createdAt", "此处的value值不重要,key重要");
firstField.put("updatedAt", "此处的value值不重要,key重要");
firstField.put("remarks", "此处的value值不重要,key重要");
fieldMap.put(1, firstField);
// response 在控制层放入方法参数中
// @RequestMapping(value = "/role/excel/export", method = RequestMethod.GET)
// public void export(HttpServletResponse response){}
HttpServletResponse response = null;
exportExcel(data, buildExcelHeaderList(headerMap),
buildExcelFieldList(fieldMap), specialKeyMap, excelName, suffix, response);
}
5.注意:
static {
String excelName = "角色信息表";
String suffix = ".xlsx";
LinkedHashMap firstHeader = new LinkedHashMap<>();
LinkedHashMap secondHeader = new LinkedHashMap<>();
LinkedHashMap thirdHeader = new LinkedHashMap<>();
LinkedHashMap> headerMap = new LinkedHashMap<>();
// 代码会将此处的value值填充到表头
firstHeader.put("1", "名称");
firstHeader.put("2", "描述");
firstHeader.put("3", "时间");
// 加入表头时间下存在创建时间和更新时间
secondHeader.put("3_1", "创建时间");
secondHeader.put("3_2", "更新时间");
// 加入创建时间下还有备注
thirdHeader.put("3_1_1", "备注");
// 然后统一放入headerMap 从1开始代表层数从1开始
headerMap.put(1, firstHeader);
headerMap.put(2, secondHeader);
headerMap.put(3, thirdHeader);
LinkedHashMap firstField = new LinkedHashMap<>();
LinkedHashMap> fieldMap = new LinkedHashMap<>();
firstField.put("name", "此处的value值不重要,key重要");
firstField.put("description", "此处的value值不重要,key重要");
firstField.put("createdAt", "此处的value值不重要,key重要");
firstField.put("updatedAt", "此处的value值不重要,key重要");
firstField.put("remarks", "此处的value值不重要,key重要");
fieldMap.put(1, firstField);
}