com.github.stupdit1t.excel.core.ExcelUtil Maven / Gradle / Ivy
package com.github.stupdit1t.excel.core;
import com.github.stupdit1t.excel.callback.InCallback;
import com.github.stupdit1t.excel.common.*;
import com.github.stupdit1t.excel.core.export.ComplexCell;
import com.github.stupdit1t.excel.core.export.ExportRules;
import com.github.stupdit1t.excel.core.export.OutColumn;
import com.github.stupdit1t.excel.core.parse.InColumn;
import com.github.stupdit1t.excel.handle.ImgHandler;
import com.github.stupdit1t.excel.handle.rule.BaseVerifyRule;
import com.github.stupdit1t.excel.style.CellPosition;
import com.github.stupdit1t.excel.style.ICellStyle;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.DataValidationConstraint.OperatorType;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.util.CellUtil;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* excel导入导出工具,也可以导出模板
*
* @author 625
*/
public class ExcelUtil {
private static final Logger LOG = LogManager.getLogger(ExcelUtil.class);
/**
* 私有
*/
private ExcelUtil() {
}
/**
* 设置打印方向
*
* @param sheet sheet页
*/
public static void printSetup(Sheet sheet) {
PrintSetup printSetup = sheet.getPrintSetup();
// 打印方向,true:横向,false:纵向
printSetup.setLandscape(true);
sheet.setFitToPage(true);
sheet.setHorizontallyCenter(true);
}
/**
* 给工作簿加密码
*
* @param workbook 工作簿
* @param password 密码
*/
public static void encryptWorkbook03(Workbook workbook, String password) {
// 2003
Biff8EncryptionKey.setCurrentUserPassword(password);
((HSSFWorkbook) workbook).writeProtectWorkbook(password, StringUtils.EMPTY);
}
/**
* 创建大数据workBook
* 避免OOM,导出速度比较慢
*
* 默认后缀 xlsx
*
* @param rowAccessWindowSize 在内存中的行数
*/
public static Workbook createBigWorkbook(int rowAccessWindowSize) {
return new SXSSFWorkbook(rowAccessWindowSize);
}
/**
* 创建空的workBook,做循环填充用
*
* @param xlsx 是否为xlsx格式
*/
public static Workbook createEmptyWorkbook(boolean xlsx) {
Workbook wb;
if (xlsx) {
// 2007
wb = new XSSFWorkbook();
} else {
// 2003
wb = new HSSFWorkbook();
}
return wb;
}
/**
* 获取导出Excel的流
*
* @param response 响应流
* @param fileName 文件名
*/
static OutputStream getDownloadStream(HttpServletResponse response, String fileName) {
try {
if (fileName.endsWith(".xlsx")) {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
} else {
response.setContentType("application/vnd.ms-excel");
}
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
return response.getOutputStream();
} catch (IOException e) {
LOG.error(e);
}
return null;
}
/**
* 导出
*
* @param workbook 工作簿
* @param response 响应
* @param password 文件密码
* @param fileName 文件名
*/
public static void export(Workbook workbook, HttpServletResponse response, String fileName, String password) {
export(workbook, getDownloadStream(response, fileName), password);
}
/**
* 导出
*
* @param out 导出流
* @param data 数据源
* @param exportRules 导出规则
*/
public static void export(OutputStream out, List data, ExportRules exportRules) {
Workbook workbook = createEmptyWorkbook(exportRules.isXlsx());
fillBook(workbook, data, exportRules);
export(workbook, out, exportRules.getPassword());
}
/**
* 导出
*
* @param workbook 工作簿
* @param outPath 删除目录
* @param password 文件密码
*/
public static void export(Workbook workbook, String outPath, String password) {
try (
Workbook wb = workbook;
OutputStream out = new FileOutputStream(outPath)
) {
export(wb, out, password);
} catch (IOException e) {
LOG.error(e);
}
}
/**
* 导出
*
* @param workbook 工作簿
* @param outputStream 流
*/
public static void export(Workbook workbook, OutputStream outputStream, String password) {
try (
Workbook wb = workbook;
OutputStream out = outputStream
) {
// 如果有密码, 且是03Excel
if (StringUtils.isNotBlank(password)) {
if (wb instanceof HSSFWorkbook) {
encryptWorkbook03(workbook, password);
} else {
// 其它版本excel
EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard);
Encryptor enc = info.getEncryptor();
enc.confirmPassword(password);
POIFSFileSystem poifsFileSystem = new POIFSFileSystem();
try {
OutputStream encOutStream = enc.getDataStream(poifsFileSystem);
wb.write(encOutStream);
encOutStream.close();
poifsFileSystem.writeFilesystem(out);
poifsFileSystem.close();
return;
} catch (GeneralSecurityException e) {
LOG.error(e);
}
}
}
wb.write(out);
if (wb instanceof SXSSFWorkbook) {
// 将此workbook对应的临时文件删除
((SXSSFWorkbook) wb).dispose();
}
} catch (IOException e) {
LOG.error(e);
}
}
/**
* 填充wb,循环填充为多个Sheet
*
* @param wb 工作簿
* @param data 数据
* @param exportRules 导出规则
*/
public static void fillBook(Workbook wb, List data, ExportRules exportRules) {
// -------------------- 全局样式处理 start ------------------------
ICellStyle[] globalStyle = exportRules.getGlobalStyle();
// 标题样式设置
Font titleFont = wb.createFont();
CellStyle titleStyleSource = wb.createCellStyle();
ICellStyle titleStyle = handleGlobalStyle(globalStyle, titleFont, titleStyleSource, CellPosition.TITLE);
// 小标题样式
Font headerFont = wb.createFont();
CellStyle headerStyleSource = wb.createCellStyle();
ICellStyle headerStyle = handleGlobalStyle(globalStyle, headerFont, headerStyleSource, CellPosition.HEADER);
// 单元格样式
Font cellFont = wb.createFont();
CellStyle cellStyleSource = wb.createCellStyle();
ICellStyle cellStyle = handleGlobalStyle(globalStyle, cellFont, cellStyleSource, CellPosition.CELL);
// 尾行样式
Font footerFont = wb.createFont();
CellStyle footerStyleSource = wb.createCellStyle();
ICellStyle footerStyle = handleGlobalStyle(globalStyle, footerFont, footerStyleSource, CellPosition.FOOTER);
// -------------------- 全局样式处理 end ------------------------
String sheetName = exportRules.getSheetName();
Sheet sheet = safeCreateSheet(wb, sheetName);
ExcelUtil.printSetup(sheet);
// ----------------------- 表头设置 start ------------------------
// 创建表头
for (int i = 0; i < exportRules.getMaxRows(); i++) {
Row row = sheet.createRow(i);
for (int j = 0; j < exportRules.getMaxColumns(); j++) {
row.createCell(j);
}
}
// 合并模式
if (exportRules.isIfMerge()) {
handleComplexHeader(exportRules, titleFont, titleStyleSource, titleStyle, headerFont, headerStyleSource, headerStyle, sheet);
} else {// 非合并
handleSimpleHeader(exportRules, titleFont, titleStyleSource, titleStyle, headerFont, headerStyleSource, headerStyle, sheet);
}
// ----------------------- 表头设置 end ------------------------
// ----------------------- 列属性设置 start -----------------------
handleColumnProperty(data, exportRules, sheet);
// ----------------------- 列属性设置 end ------------------------
// ----------------------- body设置 start ------------------------
// 画图器
Drawing> createDrawingPatriarch = safeCreateDrawing(sheet);
// 存储类的字段信息
Map, Map> clsInfo = new HashMap<>();
// 存储单元格样式信息,防止重复生成
Map cacheStyle = new HashMap<>();
// 存储单元格字体信息,防止重复生成
Map cacheFont = new HashMap<>();
// 列信息
List> fields = exportRules.getColumn();
// 纵向合并信息计算存放
Map mergerRepeatCellsMap = new HashMap<>();
List mergerRepeatCells = new ArrayList<>();
// 上一次行数据
Map lastRepeatKeyMap = new HashMap<>();
for (int i = 0; i < data.size(); i++) {
Row row = sheet.createRow(i + exportRules.getMaxRows());
if (cellStyle.getHeight() != -1) {
row.setHeight(cellStyle.getHeight());
}
// 行高自定义设置
if (exportRules.getCellHeight() != -1) {
row.setHeight(exportRules.getCellHeight());
}
T t = data.get(i);
for (int j = 0, n = 0; n < fields.size(); j++, n++) {
OutColumn column = (OutColumn) fields.get(n);
Cell cell = row.createCell(j);
cell.setCellStyle(cellStyleSource);
// 1.序号设置
if (exportRules.isAutoNum() && j == 0) {
cell.setCellValue(i + 1);
n--;
continue;
}
// 2.读取Map/Object对应字段值
if (clsInfo.get(t.getClass()) == null) {
clsInfo.put(t.getClass(), PoiCommon.getAllFields(t.getClass()));
}
Object value = readField(clsInfo, t, column.getField());
// 3.填充列值
OutColumn.Style style = column.getStyle();
if (column.getOutHandle() != null) {
style = OutColumn.Style.clone(column.getStyle());
value = column.getOutHandle().callback(value, t, style, i);
}
// 4.样式处理
setCellStyle(wb, cellFont, cacheStyle, cacheFont, style, cell, value);
// 5.设置单元格值
setCellValue(createDrawingPatriarch, value, cell);
// 6.批注添加
String comment = style.getComment() != null ? style.getComment() : column.getComment();
if (StringUtils.isNotBlank(comment)) {
setCellComment(createDrawingPatriarch, cell, comment);
}
// 7. 纵向合并判断
if (column.getMergerRepeatFieldValue() != null) {
StringBuilder repeatValue = new StringBuilder();
if (column.getMergerRepeatFieldValue().length == 1 && column.getMergerRepeatFieldValue()[0].equals(column.getField())) {
repeatValue.append(value);
} else {
for (String repeatField : column.getMergerRepeatFieldValue()) {
repeatValue.append(readField(clsInfo, t, repeatField));
}
}
String nowKey = column.getField() + repeatValue;
String lastRepeatKey = lastRepeatKeyMap.getOrDefault(column.getField(), "");
if (!nowKey.equals(lastRepeatKey)) {
// 内容不相同, 则重置上一次单元格数据, 存放合并数据
Integer[] mergerRepeatCell = mergerRepeatCellsMap.remove(lastRepeatKey);
if (mergerRepeatCell != null) {
mergerRepeatCells.add(mergerRepeatCell);
}
lastRepeatKey = nowKey;
lastRepeatKeyMap.put(column.getField(), lastRepeatKey);
}
Integer[] mergerCell = mergerRepeatCellsMap.getOrDefault(lastRepeatKey, new Integer[4]);
mergerCell[2] = j;
mergerCell[3] = j;
if (mergerCell[0] == null) {
mergerCell[0] = i + exportRules.getMaxRows();
}
mergerCell[1] = i + exportRules.getMaxRows();
// 如果是最后一行, 则直接存放合并数据
if (i == data.size() - 1) {
mergerRepeatCells.add(mergerCell);
} else {
mergerRepeatCellsMap.put(lastRepeatKey, mergerCell);
}
}
}
}
// ----------------------- body设置 end -------------------------
// ------------------------footer设置 start ------------------------
handleFooter(data, exportRules, footerFont, footerStyleSource, footerStyle, sheet);
// ------------------------footer设置 end ---------------------------
// ------------------------ 设置重复行合并 start ----------------------
for (Integer[] mergerCell : mergerRepeatCells) {
if (!mergerCell[0].equals(mergerCell[1])) {
cellMerge(sheet, mergerCell[0], mergerCell[1], mergerCell[2], mergerCell[3]);
}
}
// ------------------------ 设置重复行合并 end ------------------------
// ------------------------ 设置自定义合并 start ----------------------
List mergerCells = exportRules.getMergerCells();
if (mergerCells != null) {
for (Integer[] mergerCell : mergerCells) {
cellMerge(sheet, mergerCell[0], mergerCell[1], mergerCell[2], mergerCell[3]);
}
}
// ------------------------ 设置自定义合并 end ------------------------
}
/**
* 设置单元格的批注
*
* @param createDrawingPatriarch 画图器
* @param cell 单元格
* @param comment 批注内容
*/
private static void setCellComment(Drawing> createDrawingPatriarch, Cell cell, String comment) {
Workbook wb = cell.getSheet().getWorkbook();
// 表示需要用户添加批注
ClientAnchor clientAnchor;
RichTextString richTextString;
if (wb instanceof XSSFWorkbook) {
clientAnchor = new XSSFClientAnchor();
richTextString = new XSSFRichTextString(comment);
} else if (wb instanceof HSSFWorkbook) {
clientAnchor = new HSSFClientAnchor();
richTextString = new HSSFRichTextString(comment);
} else {
clientAnchor = new XSSFClientAnchor();
richTextString = new XSSFRichTextString(comment);
}
Comment cellComment = createDrawingPatriarch.createCellComment(clientAnchor);
cellComment.setAddress(cell.getAddress());
cellComment.setString(richTextString);
cell.setCellComment(cellComment);
}
/**
* 同步创建drawing
*
* @param sheet sheet
* @return Drawing
*/
private static synchronized Drawing> safeCreateDrawing(Sheet sheet) {
Drawing> createDrawingPatriarch;
synchronized (ExcelUtil.class) {
createDrawingPatriarch = sheet.createDrawingPatriarch();
}
return createDrawingPatriarch;
}
/**
* 同步创建sheet
*
* @param wb 工作簿
* @param sheetName sheet名字
* @return Sheet
*/
private static synchronized Sheet safeCreateSheet(Workbook wb, String sheetName) {
return sheetName != null ? wb.createSheet(sheetName) : wb.createSheet();
}
/**
* 处理单元格样式
*
* @param wb 工作簿
* @param cellFont 原字体
* @param cacheStyle 缓存样式
* @param cacheFont 缓存字体
* @param styleCustom 样式
* @param cell 单元格
* @param value 值
*/
private static void setCellStyle(Workbook wb, Font cellFont, Map cacheStyle, Map cacheFont, OutColumn.Style styleCustom, Cell cell, Object value) {
String styleCacheKey = styleCustom.getStyleCacheKey();
// 此处有值, 表示用户自定义列样式
if (styleCacheKey != null) {
CellStyle style = cacheStyle.get(styleCacheKey);
// 表示缓存无, 重新构建
if (style == null) {
style = wb.createCellStyle();
style.cloneStyleFrom(cell.getCellStyle());
// 1.水平定位
HorizontalAlignment align = styleCustom.getAlign();
if (align != null) {
style.setAlignment(align);
}
// 2.垂直定位
VerticalAlignment valign = styleCustom.getValign();
if (valign != null) {
style.setVerticalAlignment(valign);
}
// 3.字体颜色
IndexedColors color = styleCustom.getColor();
if (color != null) {
Font font = cacheFont.get(styleCacheKey);
if (font == null) {
font = wb.createFont();
PoiCommon.copyFont(font, cellFont);
cacheFont.put(styleCacheKey, font);
}
font.setColor(color.getIndex());
style.setFont(font);
}
// 4.背景色
IndexedColors backColor = styleCustom.getBackColor();
if (backColor != null) {
style.setFillForegroundColor(backColor.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
// 5. 格式化
String pattern = styleCustom.getPattern();
if (StringUtils.isNotBlank(pattern)) {
CreationHelper createHelper = wb.getCreationHelper();
style.setDataFormat(createHelper.createDataFormat().getFormat(pattern));
}
// 6. 换行显示
Boolean wrapText = styleCustom.getWrapText();
if (wrapText != null) {
style.setWrapText(wrapText);
}
cacheStyle.put(styleCacheKey, style);
}
// 最终样式设置
cell.setCellStyle(style);
}
// 如果是日期, 且用户没有设置日期格式化, 默认年月日时分秒
boolean dateValue = value instanceof Date || value instanceof LocalDate || value instanceof LocalDateTime;
if (dateValue && styleCustom.getPattern() == null) {
String cacheDateKey = "global-signal-date";
CellStyle style = cacheStyle.get(cacheDateKey);
if (style == null) {
style = wb.createCellStyle();
style.cloneStyleFrom(cell.getCellStyle());
CreationHelper createHelper = wb.getCreationHelper();
style.setDataFormat(createHelper.createDataFormat().getFormat(PoiConstant.FMT_DATE_TIME));
cacheStyle.put(cacheDateKey, style);
}
cell.setCellStyle(style);
}
// 6.高度
int height = styleCustom.getHeight();
if (height != -1) {
// 表示需要用户自定义高度
cell.getRow().setHeight((short) height);
}
}
/**
* footer设置
*
* @param data 数据
* @param exportRules 导出规则
* @param footerFont font
* @param footerStyleSource style
* @param sheet sheet
* @param footerStyle 全局样式
*/
private static void handleFooter(List data, ExportRules exportRules, Font footerFont, CellStyle footerStyleSource, ICellStyle footerStyle, Sheet sheet) {
if (exportRules.isIfFooter()) {
Workbook workbook = sheet.getWorkbook();
List footerRules = exportRules.getFooterRules();
// 构建尾行
int currRowNum = exportRules.getMaxRows() + data.size();
int[] footerNum = getFooterNum(footerRules, currRowNum);
for (int j : footerNum) {
sheet.createRow(j);
}
for (ComplexCell entry : footerRules) {
Integer[] range = entry.getLocationIndex();
String value = entry.getText();
BiConsumer fontCellStyle = entry.getStyle();
int firstRow = range[0] + currRowNum;
int lastRow = range[1] + currRowNum;
int firstCol = range[2];
int lastCol = range[3];
CellStyle styleNew;
if (fontCellStyle != null) {
// 自定义header单元格样式
styleNew = workbook.createCellStyle();
Font fontNew = workbook.createFont();
PoiCommon.copyStyleAndFont(styleNew, fontNew, footerStyleSource, footerFont);
fontCellStyle.accept(fontNew, styleNew);
} else {
styleNew = footerStyleSource;
}
Cell cell = CellUtil.createCell(sheet.getRow(firstRow), firstCol, value, styleNew);
if (value.startsWith("=")) {
try {
cell.setCellFormula(value.substring(1));
}catch (Exception e){
cell.setCellValue(value);;
}
}
if ((lastRow - firstRow) != 0 || (lastCol - firstCol) != 0) {
cellMerge(sheet, firstRow, lastRow, firstCol, lastCol);
}
// 行高自定义设置
for (int i = firstRow; i <= lastRow; i++) {
if (footerStyle.getHeight() != -1) {
sheet.getRow(i).setHeight(footerStyle.getHeight());
}
if (exportRules.getFooterHeight() != -1) {
sheet.getRow(i).setHeight(exportRules.getFooterHeight());
}
}
}
}
}
/**
* 列属性设置
*
* @param data 数据
* @param exportRules 导出规则
* @param sheet sheet
*/
private static void handleColumnProperty(List data, ExportRules exportRules, Sheet sheet) {
// ----------------------- 列属性设置 start--------------------
List> fields = exportRules.getColumn();
int columnWidth = exportRules.getColumnWidth();
int autoNumColumnWidth = exportRules.getAutoNumColumnWidth();
for (int i = 0, j = 0; i < fields.size(); i++, j++) {
// 0.每一列默认单元格样式设置
// 1.width设置
if (exportRules.isAutoNum() && j == 0) {
j++;
sheet.setColumnWidth(0, autoNumColumnWidth);
}
OutColumn> column = fields.get(i);
// 1.1是否自动列宽
int width = column.getStyle().getWidth();
if (width != -1) {
sheet.setColumnWidth(j, width);
} else {
try {
// 1.2根据maxRows,获取表头的值设置宽度
if (columnWidth != -1) {
sheet.setColumnWidth(j, columnWidth);
} else {
Row row = sheet.getRow(exportRules.getMaxRows() - 1);
String headerValue = row.getCell(j).getStringCellValue();
if (StringUtils.isBlank(headerValue)) {
row = sheet.getRow(exportRules.getMaxRows() - 2);
headerValue = row.getCell(j).getStringCellValue();
}
sheet.setColumnWidth(j, headerValue.getBytes().length * 256);
}
} catch (Exception e) {
if (exportRules.isAutoNum()) {
LOG.error("请确认表头数量和列数量一致! ");
throw new UnsupportedOperationException("自动序号设置错误,请确认在header添加序号列");
} else {
LOG.error("请确认表头数量和列数量一致! ");
throw e;
}
}
}
// 2.downDown设置
int lastRow = (exportRules.getMaxRows() - 1) + data.size();
lastRow = lastRow == (exportRules.getMaxRows() - 1) ? PoiConstant.MAX_FILL_COL : lastRow;
String[] dropdown = column.getDropdown();
if (dropdown != null && dropdown.length > 0) {
sheet.addValidationData(createDropDownValidation(sheet, dropdown, j, exportRules.getMaxRows(), lastRow));
}
// 3.时间校验
String date = column.getVerifyDate();
if (date != null) {
String[] split = date.split("@");
String info = null;
if (split.length == 2) {
info = split[1];
}
String[] split1 = split[0].split("~");
if (split1.length < 2) {
throw new IllegalArgumentException("时间校验表达式不正确,请填写如" + column.getStyle().getPattern() + "的值!");
}
try {
sheet.addValidationData(createDateValidation(sheet, column.getStyle().getPattern(), split1[0], split1[1], info, j, exportRules.getMaxRows(), lastRow));
} catch (ParseException e) {
LOG.error(e);
throw new IllegalArgumentException("时间校验表达式不正确,请填写如" + column.getStyle().getPattern() + "的值!");
} catch (Exception e) {
LOG.error(e);
}
}
// 4.整数数字校验
String num = column.getVerifyIntNum();
if (num != null) {
String[] split = num.split("@");
String info = null;
if (split.length == 2) {
info = split[1];
}
String[] split1 = split[0].split("~");
if (split1.length < 2) {
throw new IllegalArgumentException("数字表达式不正确,请填写如10~30的值!");
}
sheet.addValidationData(createNumValidation(sheet, split1[0], split1[1], info, j, exportRules.getMaxRows(), lastRow));
}
// 4.浮点数字校验
String floatNum = column.getVerifyFloatNum();
if (floatNum != null) {
String[] split = floatNum.split("@");
String info = null;
if (split.length == 2) {
info = split[1];
}
String[] split1 = split[0].split("~");
if (split1.length < 2) {
throw new IllegalArgumentException("数字表达式不正确,请填写如10.0~30.0的值!");
}
sheet.addValidationData(createFloatValidation(sheet, split1[0], split1[1], info, j, exportRules.getMaxRows(), lastRow));
}
// 5.自定义校验
String custom = column.getVerifyCustom();
if (custom != null) {
String[] split = custom.split("@");
String info = null;
if (split.length == 2) {
info = split[1];
}
sheet.addValidationData(createCustomValidation(sheet, split[0], info, j, exportRules.getMaxRows(), lastRow));
}
// 6.文本长度校验
String text = column.getVerifyText();
if (text != null) {
String[] split1 = text.split("@");
String info = null;
if (split1.length == 2) {
info = split1[1];
}
String[] split2 = split1[0].split("~");
if (split2.length < 2) {
throw new IllegalArgumentException("文本长度校验格式不正确,请设置如3~10格式!");
}
sheet.addValidationData(createTextLengthValidation(sheet, split2[0], split2[1], info, j, exportRules.getMaxRows(), lastRow));
}
}
}
/**
* 简单表头设计
*
* @param exportRules 导出规则
* @param titleFont 大标题字体
* @param titleStyleSource 大标题样式
* @param titleStyle 大标题自定义样式处理
* @param headerFont 标题字体
* @param headerStyleSource 标题样式
* @param headerStyle 标题自定义样式处理
* @param sheet sheet
*/
private static void handleSimpleHeader(ExportRules exportRules, Font titleFont, CellStyle titleStyleSource, ICellStyle titleStyle, Font headerFont, CellStyle headerStyleSource, ICellStyle headerStyle, Sheet sheet) {
// 1. 冻结表头
if (exportRules.isFreezeHeader()) {
sheet.createFreezePane(0, exportRules.getMaxRows(), 0, exportRules.getMaxRows());
}
// 2. title 内容设置和行高
if (exportRules.getTitle() != null) {
// title全局行高
if (titleStyle.getHeight() != -1) {
sheet.getRow(0).setHeight(titleStyle.getHeight());
}
// title行高自定义设置
if (exportRules.getTitleHeight() != -1) {
sheet.getRow(0).setHeight(exportRules.getTitleHeight());
}
CellUtil.createCell(sheet.getRow(0), 0, exportRules.getTitle(), titleStyleSource);
cellMerge(sheet, 0, 0, 0, exportRules.getMaxColumns());
}
// 3.header设置和行高
int headerIndex = exportRules.getTitle() == null ? 0 : 1;
// header全局行高
if (headerStyle.getHeight() != -1) {
sheet.getRow(headerIndex).setHeight(headerStyle.getHeight());
}
// header行高自定义设置
if (exportRules.getHeaderHeight() != -1) {
sheet.getRow(headerIndex).setHeight(exportRules.getHeaderHeight());
}
LinkedHashMap> headerMap = exportRules.getSimpleHeader();
List header = new ArrayList<>(headerMap.keySet());
for (int i = 0; i < header.size(); i++) {
String text = header.get(i);
CellStyle styleNew;
BiConsumer fontCellStyle = headerMap.get(text);
if (fontCellStyle != null) {
// 自定义header单元格样式
styleNew = sheet.getWorkbook().createCellStyle();
Font fontNew = sheet.getWorkbook().createFont();
PoiCommon.copyStyleAndFont(styleNew, fontNew, headerStyleSource, headerFont);
fontCellStyle.accept(fontNew, styleNew);
} else {
styleNew = headerStyleSource;
}
// 处理重复表头
if (text.startsWith("$")) {
text = text.replaceAll("^\\$_\\d+_", "");
}
CellUtil.createCell(sheet.getRow(headerIndex), i, text, styleNew);
}
}
/**
* 复杂表头设计
*
* @param exportRules 导出规则
* @param titleFont 大标题字体
* @param titleStyleSource 大标题样式
* @param titleStyle 大标题自定义样式处理
* @param headerFont 标题字体
* @param headerStyleSource 标题样式
* @param headerStyle 标题自定义样式处理
* @param sheet sheet
*/
private static void handleComplexHeader(ExportRules exportRules, Font titleFont, CellStyle titleStyleSource, ICellStyle titleStyle, Font headerFont, CellStyle headerStyleSource, ICellStyle headerStyle, Sheet sheet) {
// 冻结表头
if (exportRules.isFreezeHeader()) {
sheet.createFreezePane(0, exportRules.getMaxRows(), 0, exportRules.getMaxRows());
}
// header
List complexHeader = exportRules.getComplexHeader();
for (ComplexCell complexCell : complexHeader) {
Integer[] range = complexCell.getLocationIndex();
// 合并表头
int firstRow = range[0];
int lastRow = range[1];
int firstCol = range[2];
int lastCol = range[3];
CellStyle styleTemp;
Font fontTemp;
if ((exportRules.getMaxColumns() - 1) == lastCol - firstCol && firstRow == 0) {
// 占满全格, 且第一行开始为表头
for (int i = firstRow; i <= lastRow; i++) {
if (titleStyle.getHeight() != -1) {
sheet.getRow(i).setHeight(titleStyle.getHeight());
}
// 行高自定义设置
if (exportRules.getTitleHeight() != -1) {
sheet.getRow(i).setHeight(exportRules.getTitleHeight());
}
}
styleTemp = titleStyleSource;
fontTemp = titleFont;
} else {
// 没有大表头, 普通合并格
for (int i = firstRow; i <= lastRow; i++) {
if (headerStyle.getHeight() != -1) {
sheet.getRow(i).setHeight(headerStyle.getHeight());
}
// 行高自定义设置
if (exportRules.getHeaderHeight() != -1) {
sheet.getRow(i).setHeight(exportRules.getHeaderHeight());
}
}
styleTemp = headerStyleSource;
fontTemp = headerFont;
}
CellStyle styleNew = styleTemp;
BiConsumer fontCellStyle = complexCell.getStyle();
if (fontCellStyle != null) {
// 自定义header单元格样式
styleNew = sheet.getWorkbook().createCellStyle();
Font fontNew = sheet.getWorkbook().createFont();
PoiCommon.copyStyleAndFont(styleNew, fontNew, styleTemp, fontTemp);
fontCellStyle.accept(fontNew, styleNew);
}
CellUtil.createCell(sheet.getRow(firstRow), firstCol, complexCell.getText(), styleNew);
if ((lastRow - firstRow) != 0 || (lastCol - firstCol) != 0) {
cellMerge(sheet, firstRow, lastRow, firstCol, lastCol);
}
}
}
/**
* 合并单元格
*
* @param sheet sheet
* @param firstRow 卡死行
* @param lastRow 结束行
* @param firstCol 开始列
* @param lastCol 结束列
*/
private static void cellMerge(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
sheet.addMergedRegion(cra);
RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet);
RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet);
RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet);
RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet);
}
/**
* 全局样式处理
*
* @param globalStyle 全局样式
* @param font 字体
* @param cellStyle 样式
* @param cellPosition 位置
*/
private static ICellStyle handleGlobalStyle(ICellStyle[] globalStyle, Font font, CellStyle cellStyle, CellPosition cellPosition) {
ICellStyle titleStyle = ICellStyle.getCellStyleByPosition(cellPosition, globalStyle);
cellStyle.setFont(font);
titleStyle.handleStyle(font, cellStyle);
return titleStyle;
}
/**
* 读取规则excel数据内容为map
*
* @param filePath 文件路径
* @param poiSheetArea 数据区域
* @param columns 数据列定义
* @param map 回调数据行
* @param rowClass 数据类
* @return PoiResult
*/
public static PoiResult readSheet(String filePath, PoiSheetDataArea poiSheetArea, Map> columns, InCallback map, Class rowClass) {
try (InputStream is = new FileInputStream(filePath)) {
return readSheet(is, poiSheetArea, columns, map, rowClass);
} catch (Exception e) {
LOG.error(e);
return PoiResult.fail(new ErrorMessage(e));
}
}
/**
* 读取规则excel数据内容为map
*
* @param filePath 文件路径
* @param poiSheetArea 数据区域
* @param columns 数据列定义
* @param map 回调数据行
* @param rowClass 数据类
* @return PoiResult
*/
public static PoiResult readSheet(String filePath, String password, PoiSheetDataArea poiSheetArea, Map> columns, InCallback map, Class rowClass) {
try (InputStream is = new FileInputStream(filePath)) {
return readSheet(is, password, poiSheetArea, columns, map, rowClass);
} catch (Exception e) {
LOG.error(e);
return PoiResult.fail(new ErrorMessage(e));
}
}
/**
* 读取规则excel数据内容为map
*
* @param is 文件流
* @param poiSheetArea 数据区域
* @param columns 数据列定义
* @param map 回调数据行
* @param rowClass 数据类
* @return PoiResult
*/
public static PoiResult readSheet(InputStream is, PoiSheetDataArea poiSheetArea, Map> columns, InCallback map, Class rowClass) {
try (Workbook wb = WorkbookFactory.create(is)) {
String sheetName = poiSheetArea.getSheetName();
Sheet sheet;
if (StringUtils.isBlank(sheetName)) {
sheet = wb.getSheetAt(poiSheetArea.getSheetIndex());
} else {
sheet = wb.getSheet(sheetName);
}
return readSheet(sheet, poiSheetArea.getHeaderRowCount(), poiSheetArea.getFooterRowCount(), columns, map, rowClass);
} catch (Exception e) {
LOG.error(e);
return PoiResult.fail(new ErrorMessage(e));
}
}
/**
* 读取规则excel数据内容为map
*
* @param is 文件流
* @param poiSheetArea 数据区域
* @param columns 数据列定义
* @param map 回调数据行
* @param rowClass 数据类
* @return PoiResult
*/
public static PoiResult readSheet(InputStream is, String password, PoiSheetDataArea poiSheetArea, Map> columns, InCallback map, Class rowClass) {
try (Workbook wb = WorkbookFactory.create(is, password)) {
String sheetName = poiSheetArea.getSheetName();
Sheet sheet;
if (StringUtils.isBlank(sheetName)) {
sheet = wb.getSheetAt(poiSheetArea.getSheetIndex());
} else {
sheet = wb.getSheet(sheetName);
}
return readSheet(sheet, poiSheetArea.getHeaderRowCount(), poiSheetArea.getFooterRowCount(), columns, map, rowClass);
} catch (Exception e) {
LOG.error(e);
return PoiResult.fail(new ErrorMessage(e));
}
}
/**
* 读取规则excel数据内容为map
*
* @param sheet sheet页
* @param dataStartRow 起始行
* @param dataEndRowCount 尾部非数据行数量
* @return PoiResult
*/
public static PoiResult readSheet(Sheet sheet, int dataStartRow, int dataEndRowCount, Map> columns, InCallback map, Class rowClass) {
boolean mapClass = PoiCommon.isMapData(rowClass);
PoiResult rsp = new PoiResult<>();
List beans = new ArrayList<>();
// 获取excel中所有图片
Set hasImgField = new HashSet<>();
Map pictures = null;
Collection> values = columns.values();
int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
for (InColumn> inColumn : values) {
BaseVerifyRule, ?> cellVerify = inColumn.getCellVerifyRule();
if (cellVerify instanceof ImgHandler) {
if (pictures == null) {
pictures = getSheetPictures(sheetIndex, sheet);
}
hasImgField.add(inColumn.getField());
}
}
// 公式计算初始化
FormulaEvaluator formulaEvaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator();
int rowStart = sheet.getFirstRowNum() + dataStartRow;
// 获取真实的数据行尾数
int rowEnd = getLastRealLastRow(sheet.getRow(sheet.getLastRowNum())) - dataEndRowCount;
List error = new ArrayList<>();
try {
for (int j = rowStart; j <= rowEnd; j++) {
T data = rowClass.newInstance();
Row row = sheet.getRow(j);
if (row == null) {
continue;
}
int lastCellNum = columns.size() == 0 ? row.getLastCellNum() : columns.size();
for (int k = 0; k < lastCellNum; k++) {
String fieldName;
// 列名称获取
String columnIndexChar = PoiConstant.numsRefCell.get(k);
String location = columnIndexChar + (j + 1);
try {
InColumn> inColumn = columns.get(columnIndexChar);
Object cellValue;
if (inColumn != null) {
fieldName = inColumn.getField();
} else {
// 只有map的情况下, 才使用列字符串
if (mapClass) {
fieldName = columnIndexChar;
} else {
fieldName = null;
}
}
if (fieldName == null) {
continue;
}
if (pictures != null && hasImgField.contains(fieldName)) {
String pictureIndex = sheetIndex + "," + j + "," + k;
PictureData remove = pictures.remove(pictureIndex);
cellValue = remove == null ? null : remove.getData();
} else {
cellValue = getCellValue(row, k, formulaEvaluator);
}
// 校验类型转换处理
if (inColumn != null) {
cellValue = inColumn.getCellVerifyRule().handle(j, k, cellValue);
}
if (mapClass) {
((Map) data).put(fieldName, cellValue);
} else {
FieldUtils.writeField(data, fieldName, cellValue, true);
}
} catch (Exception e) {
error.add(new ErrorMessage(location, j, k, e));
}
}
// 有效, 回调处理加入
if (map != null) {
int rowNum = j + 1;
try {
map.callback(data, rowNum);
} catch (Exception e) {
error.add(new ErrorMessage("第" + rowNum + "行", j, -1, e));
}
}
if (error.isEmpty()) {
beans.add(data);
}
}
} catch (Exception e) {
LOG.error(e);
error.add(new ErrorMessage(e));
} finally {
// throw parse exception
if (error.size() > 0) {
rsp.setHasError(true);
}
rsp.setData(beans);
rsp.setError(error);
}
// 返回结果
return rsp;
}
/**
* 读取excel,替换内置变量
*
* @param filePath 文件路径
* @param variable 内置变量
*/
public static Workbook readExcelWrite(String filePath, Map variable) {
try (FileInputStream is = new FileInputStream(filePath)) {
return readExcelWrite(is, variable);
} catch (IOException e) {
LOG.error(e);
}
return null;
}
/**
* 读取excel,替换内置变量
*
* @param filePath 文件路径
* @param password 文件密码
* @param variable 内置变量
*/
public static Workbook readExcelWrite(String filePath, String password, Map variable) {
try (FileInputStream is = new FileInputStream(filePath)) {
return readExcelWrite(is, password, variable);
} catch (IOException e) {
LOG.error(e);
}
return null;
}
/**
* 读取excel,替换内置变量
*
* @param is excel文件流
* @param variable 内置变量
*/
public static Workbook readExcelWrite(InputStream is, Map variable) {
try {
Workbook wb = WorkbookFactory.create(is);
return readExcelWrite(wb, variable);
} catch (IOException e) {
LOG.error(e);
}
return null;
}
/**
* 读取excel,替换内置变量
*
* @param is excel文件流
* @param password 文件密码
* @param variable 内置变量
*/
public static Workbook readExcelWrite(InputStream is, String password, Map variable) {
try {
Workbook wb = WorkbookFactory.create(is);
if (password != null) {
wb = WorkbookFactory.create(is, password);
}
return readExcelWrite(wb, variable);
} catch (IOException e) {
LOG.error(e);
}
return null;
}
/**
* 读取excel,替换内置变量
*
* @param workbook excel对象
* @param variable 内置变量
*/
private static Workbook readExcelWrite(Workbook workbook, Map variable) {
int numberOfSheets = workbook.getNumberOfSheets();
FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
for (int i = 0; i < numberOfSheets; i++) {
Sheet sheet = workbook.getSheetAt(i);
Drawing> createDrawingPatriarch = safeCreateDrawing(sheet);
Row lastRow = sheet.getRow(sheet.getLastRowNum());
int lastRealLastRow = getLastRealLastRow(lastRow);
Set vars = variable.keySet();
for (int j = 0; j <= lastRealLastRow; j++) {
Row row = sheet.getRow(j);
if (row == null) {
continue;
}
short lastCellNum = row.getLastCellNum();
for (short k = 0; k < lastCellNum; k++) {
Object cellValue = getCellValue(row, k, formulaEvaluator);
if (cellValue instanceof String) {
String cellValueStr = (String) cellValue;
if (!cellValueStr.contains("$")) {
continue;
}
for (String var : vars) {
String keyVar = "${" + var + "}";
if(!cellValueStr.contains(keyVar)){
continue;
}
Object value = variable.get(var);
if(value instanceof byte[]) {
byte[] data = (byte[]) value;
// 插入图片
ClientAnchor anchor;
int add1;
if (workbook instanceof XSSFWorkbook) {
anchor = new XSSFClientAnchor(0, 0, 0, 0, k, j, k + 1, j + 1);
add1 = workbook.addPicture(data, XSSFWorkbook.PICTURE_TYPE_PNG);
} else if (workbook instanceof HSSFWorkbook) {
anchor = new HSSFClientAnchor(0, 0, 0, 0, k, j, (short) (k + 1), j + 1);
add1 = workbook.addPicture(data, SXSSFWorkbook.PICTURE_TYPE_PNG);
} else {
anchor = new XSSFClientAnchor(0, 0, 0, 0, k, j, (short) (k + 1), j + 1);
add1 = workbook.addPicture(data, XSSFWorkbook.PICTURE_TYPE_PNG);
}
createDrawingPatriarch.createPicture(anchor, add1);
cellValueStr = cellValueStr.replace(keyVar, "");
}else{
// 非图片,一律按字符串填充
cellValueStr = cellValueStr.replace(keyVar, String.valueOf(value));
}
if (cellValueStr.startsWith("=")) {
try {
row.getCell(k).setCellFormula(cellValueStr.substring(1));
}catch (Exception e){
row.getCell(k).setCellValue(cellValueStr);
}
} else {
row.getCell(k).setCellValue(cellValueStr);
}
}
}
}
}
}
return workbook;
}
/**
* 获取真实的数据行
*
* @param row 单元格
* @return int
*/
private static int getLastRealLastRow(Row row) {
Sheet sheet = row.getSheet();
short lastCellNum = row.getLastCellNum();
if (lastCellNum == -1) {
int rowNum = row.getRowNum();
Row newRow = sheet.getRow(--rowNum);
while (newRow == null) {
newRow = sheet.getRow(--rowNum);
}
return getLastRealLastRow(newRow);
} else {
int blankCell = 0;
for (int i = 0; i < lastCellNum; i++) {
Cell cell = row.getCell(i);
if (cell == null || cell.getCellType() == CellType.BLANK) {
blankCell++;
}
}
if (blankCell >= lastCellNum) {
int rowNum = row.getRowNum();
Row newRow = sheet.getRow(--rowNum);
while (newRow == null) {
newRow = sheet.getRow(--rowNum);
}
return getLastRealLastRow(newRow);
}
}
return row.getRowNum();
}
/**
* 读取字段的值
*
* @param clsInfo 类信息
* @param t 当前值
* @param fields 字段名称
* @return Object
*/
private static Object readField(Map, Map> clsInfo, Object t, String fields) {
// 读取子属性
String[] split = fields.split("\\.");
Object value = t;
for (int i = 0; i < split.length; i++) {
value = readObjectFieldValue(value, split[i], clsInfo);
// 属性为空跳出
if (value == null) {
return "";
}
if (i == split.length - 1) {
return value;
}
}
return "";
}
/**
* 读取object的属性
*
* @param t 对象
* @param key field字段
* @param clsInfo 类信息
* @return Object
*/
private static Object readObjectFieldValue(Object t, String key, Map, Map> clsInfo) {
try {
if (t instanceof Map) {
t = ((Map, ?>) t).get(key);
} else {
Class> subCls = t.getClass();
Map subField = clsInfo.get(subCls);
if (subField == null) {
subField = PoiCommon.getAllFields(subCls);
clsInfo.put(subCls, subField);
}
Field field = subField.get(key);
if (field == null) {
// 为方法,不是属性
char[] charName = key.toCharArray();
charName[0] -= 32;
String methodName = "get" + String.valueOf(charName);
Method method = subCls.getMethod(methodName);
t = method.invoke(t);
} else {
t = field.get(t);
}
}
} catch (Exception e) {
LOG.error(e);
t = null;
}
return t;
}
/**
* 给单元格设置值
*
* @param createDrawingPatriarch 画图器
* @param value 单元格值
* @param cell 单元格
*/
private static void setCellValue(Drawing> createDrawingPatriarch, Object value, Cell cell) {
Workbook workbook = cell.getSheet().getWorkbook();
// 8.值设置, 判断值的类型后进行强制类型转换.再设置单元格格式
if (value instanceof String) {
// 判断是否是公式
String strValue = String.valueOf(value);
if (strValue.startsWith("=")) {
try {
cell.setCellFormula(strValue.substring(1));
} catch (Exception e) {
cell.setCellValue(strValue);
}
} else {
cell.setCellValue(strValue);
}
} else if (value instanceof Number) {
// 处理整形自动不展示小数点
cell.setCellValue(((Number) value).doubleValue());
} else if (value instanceof Date || value instanceof LocalDate || value instanceof LocalDateTime) {
if (value instanceof Date) {
Date date = (Date) value;
cell.setCellValue(date);
} else if (value instanceof LocalDateTime) {
LocalDateTime date = (LocalDateTime) value;
cell.setCellValue(date);
} else {
LocalDate date = (LocalDate) value;
cell.setCellValue(date);
}
} else if (value instanceof byte[]) {
byte[] data = (byte[]) value;
// 5.1anchor主要用于设置图片的属性
short x = (short) cell.getColumnIndex();
int y = cell.getRowIndex();
// 5.2插入图片
ClientAnchor anchor;
int add1;
if (workbook instanceof XSSFWorkbook) {
anchor = new XSSFClientAnchor(0, 0, 0, 0, x, y, x + 1, y + 1);
add1 = workbook.addPicture(data, XSSFWorkbook.PICTURE_TYPE_PNG);
} else if (workbook instanceof HSSFWorkbook) {
anchor = new HSSFClientAnchor(0, 0, 0, 0, x, y, (short) (x + 1), y + 1);
add1 = workbook.addPicture(data, SXSSFWorkbook.PICTURE_TYPE_PNG);
} else {
anchor = new XSSFClientAnchor(0, 0, 0, 0, x, y, (short) (x + 1), y + 1);
add1 = workbook.addPicture(data, XSSFWorkbook.PICTURE_TYPE_PNG);
}
createDrawingPatriarch.createPicture(anchor, add1);
cell.setCellValue("");
} else if (value == null) {
cell.setCellValue("");
} else {
cell.setCellValue(String.valueOf(value));
}
}
/**
* 根据页脚数据获得行号
*
* @param entries 规则
* @param currRowNum 当前行
* @return int[]
*/
private static int[] getFooterNum(List entries, int currRowNum) {
int row = 0;
List rules = entries.stream().map(ComplexCell::getLocationIndex).collect(Collectors.toList());
for (Integer[] range : rules) {
int a = range[1] + 1;
row = Math.max(a, row);
}
int[] footerNum = new int[row];
for (int i = 0; i < row; i++) {
footerNum[i] = currRowNum + i;
}
return footerNum;
}
/**
* 获取单元格的值
*
* @param r 当前行
* @param cellNum 单元格号
* @return Object
*/
private static Object getCellValue(Row r, int cellNum, FormulaEvaluator formulaEvaluator) {
// 缺失列处理政策
Cell cell = r.getCell(cellNum, MissingCellPolicy.CREATE_NULL_AS_BLANK);
Object obj = null;
CellType cellType = cell.getCellType();
switch (cellType) {
case STRING:
obj = cell.getStringCellValue();
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
obj = cell.getDateCellValue();
} else {
obj = cell.getNumericCellValue();
}
break;
case BOOLEAN:
obj = cell.getBooleanCellValue();
break;
case FORMULA:
// 拿到计算公式eval, 捕捉公式错误异常
try {
CellValue evaluate = formulaEvaluator.evaluate(cell);
switch (evaluate.getCellType()) {
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
obj = cell.getDateCellValue();
} else {
obj = cell.getNumericCellValue();
}
break;
case STRING:
obj = evaluate.getStringValue();
break;
default:
obj = cell.getStringCellValue();
}
} catch (Exception e) {
obj = cell.getStringCellValue();
LOG.error("公式有误:{0}", e);
}
break;
case BLANK:
obj = "";
break;
default:
obj = "";
break;
}
return obj;
}
/**
* 获取Excel2003图片
*
* @param sheetNum 当前sheet下标
* @param sheet 当前sheet对象
* @return Map key:图片单元格索引(0-sheet下标,1-列号,1-行号)String,value:图片流PictureData
*/
private static Map getSheetPictures(int sheetNum, Sheet sheet) {
if (sheet instanceof HSSFSheet) {
HSSFSheet sheetHssf = (HSSFSheet) sheet;
return getSheetPictures03(sheetNum, sheetHssf);
} else {
XSSFSheet sheetXssf = (XSSFSheet) sheet;
return getSheetPictures07(sheetNum, sheetXssf);
}
}
/**
* 获取Excel2003图片
*
* @param sheetNum 当前sheet编号
* @param sheet 当前sheet对象
* @return Map key:图片单元格索引(0-sheet下标,1-列号,1-行号)String,value:图片流PictureData
*/
private static Map getSheetPictures03(int sheetNum, HSSFSheet sheet) {
Map sheetIndexPicMap = new HashMap<>();
List pictures = sheet.getWorkbook().getAllPictures();
if (pictures.isEmpty()) {
return sheetIndexPicMap;
}
HSSFPatriarch drawingPatriarch = sheet.getDrawingPatriarch();
if (drawingPatriarch == null) {
return sheetIndexPicMap;
}
for (HSSFShape shape : drawingPatriarch.getChildren()) {
HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
if (shape instanceof HSSFPicture) {
HSSFPicture pic = (HSSFPicture) shape;
int pictureIndex = pic.getPictureIndex() - 1;
HSSFPictureData picData = pictures.get(pictureIndex);
String picIndex = sheetNum + "," + anchor.getRow1() + "," + anchor.getCol1();
sheetIndexPicMap.put(picIndex, picData);
}
}
return sheetIndexPicMap;
}
/**
* 获取Excel2007图片
*
* @param sheetNum 当前sheet编号
* @param sheet 当前sheet对象
* @return Map key:图片单元格索引(0,1,1)String,value:图片流PictureData
*/
private static Map getSheetPictures07(int sheetNum, XSSFSheet sheet) {
Map sheetIndexPicMap = new HashMap<>();
for (POIXMLDocumentPart dr : sheet.getRelations()) {
if (dr instanceof XSSFDrawing) {
XSSFDrawing drawing = (XSSFDrawing) dr;
List shapes = drawing.getShapes();
for (XSSFShape shape : shapes) {
if (shape instanceof XSSFPicture) {
XSSFPicture pic = (XSSFPicture) shape;
XSSFClientAnchor anchor = pic.getClientAnchor();
CTMarker ctMarker = anchor.getFrom();
String picIndex = sheetNum + "," + ctMarker.getRow() + "," + ctMarker.getCol();
sheetIndexPicMap.put(picIndex, pic.getPictureData());
}
}
}
}
return sheetIndexPicMap;
}
/**
* excel添加下拉数据校验
*
* @param sheet 哪个 sheet 页添加校验
* @param dataSource 数据源数组
* @param col 第几列校验(0开始)
* @param firstRow 开始行
* @param lastRow 结束行
* @return DataValidation
*/
private static synchronized DataValidation createDropDownValidation(Sheet sheet, String[] dataSource, int col, int firstRow, int lastRow) {
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(firstRow, lastRow, col, col);
DataValidationHelper helper = sheet.getDataValidationHelper();
Workbook workbook = sheet.getWorkbook();
Sheet hidden = workbook.getSheet("hidden");
if (hidden == null) {
hidden = workbook.createSheet("hidden");
}
String hash = "H" + Objects.hash(dataSource);
// 1.检测下拉框是否已经创建过
int dropDownCol = 0;
Row fieldRow = hidden.getRow(0);
boolean find = false;
if (fieldRow != null) {
for (int i = 0; i < fieldRow.getLastCellNum(); i++) {
// 标题相等,已经创建过
if (fieldRow.getCell(i) != null && hash.equals(fieldRow.getCell(i).getStringCellValue())) {
dropDownCol = i;
find = true;
}
}
if (!find) {
dropDownCol = fieldRow.getLastCellNum();
}
}
String colLetter = PoiConstant.numsRefCell.get(dropDownCol);
// 2.创建下拉框引用值
if (!find) {
for (int i = 0; i < dataSource.length + 1; i++) {
// 设置单元格值
Row row = hidden.getRow(i);
if (row == null) {
row = hidden.createRow(i);
}
Cell cell = row.getCell(dropDownCol);
if (cell == null) {
cell = row.createCell(dropDownCol);
}
if (i == 0) {
// 如果是0号位, 设置单元格值为字段
cell.setCellValue(hash);
} else {
cell.setCellValue(dataSource[i - 1]);
}
}
}
// 3.设置表达式
String formula = "hidden!$" + colLetter + "$2:$" + colLetter + "$" + (dataSource.length + 1);
DataValidationConstraint constraint = helper.createFormulaListConstraint(formula);
workbook.setSheetHidden(workbook.getSheetIndex(hidden), true);
DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
dataValidation.setEmptyCellAllowed(true);
dataValidation.setShowPromptBox(true);
dataValidation.createPromptBox("提示", "只能选择下拉框里面的数据");
return dataValidation;
}
/**
* excel添加时间数据校验
*
* @param sheet 哪个 sheet 页添加校验
* @param start 開始
* @param end 结束
* @param info 提示信息
* @param col 第几列校验(0开始)
* @param maxRow 表头占用几行
* @return DataValidation
*/
private static DataValidation createDateValidation(Sheet sheet, String pattern, String start, String end, String info, int col, int maxRow, int lastRow) throws Exception {
// 1.设置验证
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(maxRow, lastRow, col, col);
DataValidationHelper helper = sheet.getDataValidationHelper();
Calendar cal = Calendar.getInstance();
Date startDate = DateUtils.parseDate(start, pattern);
Date endDate = DateUtils.parseDate(end, pattern);
cal.setTime(startDate);
String formulaStart = "=DATE(" + cal.get(Calendar.YEAR) + "," + (cal.get(Calendar.MONTH) + 1) + "," + cal.get(Calendar.DATE) + ")";
cal.setTime(endDate);
String formulaEnd = "=DATE(" + cal.get(Calendar.YEAR) + "," + (cal.get(Calendar.MONTH) + 1) + "," + cal.get(Calendar.DATE) + ")";
DataValidationConstraint constraint = helper.createDateConstraint(OperatorType.BETWEEN, formulaStart, formulaEnd, pattern);
DataValidation dataValidation = handleMultiVersion(info, cellRangeAddressList, helper, constraint);
// 2.设置单元格格式
Workbook workbook = sheet.getWorkbook();
CellStyle style = workbook.createCellStyle();
CreationHelper createHelper = workbook.getCreationHelper();
style.setDataFormat(createHelper.createDataFormat().getFormat(pattern));
sheet.setDefaultColumnStyle(col, style);
return dataValidation;
}
/**
* 兼容性问题处理
*
* @param info 提示消息
* @param cellRangeAddressList 地址
* @param helper 验证器
* @param constraint 验证
* @return DataValidation
*/
private static DataValidation handleMultiVersion(String info, CellRangeAddressList cellRangeAddressList, DataValidationHelper helper, DataValidationConstraint constraint) {
DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
dataValidation.setEmptyCellAllowed(true);
dataValidation.setShowPromptBox(true);
if (info != null) {
dataValidation.createPromptBox("提示", info);
}
return dataValidation;
}
/**
* excel添加数字校验
*
* @param sheet 哪个 sheet 页添加校验
* @param minNum 最小值
* @param maxNum 最大值
* @param info 提示信息
* @param col 第几列校验(0开始)
* @param maxRow 表头占用几行
* @return DataValidation
*/
private static DataValidation createNumValidation(Sheet sheet, String minNum, String maxNum, String info, int col, int maxRow, int lastRow) {
// 1.设置验证
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(maxRow, lastRow, col, col);
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createIntegerConstraint(OperatorType.BETWEEN, minNum, maxNum);
return handleMultiVersion(info, cellRangeAddressList, helper, constraint);
}
/**
* excel添加数字校验
*
* @param sheet 哪个 sheet 页添加校验
* @param minNum 最小值
* @param maxNum 最大值
* @param col 第几列校验(0开始)
* @param maxRow 表头占用几行
* @return DataValidation
*/
private static DataValidation createFloatValidation(Sheet sheet, String minNum, String maxNum, String info, int col, int maxRow, int lastRow) {
// 1.设置验证
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(maxRow, lastRow, col, col);
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createDecimalConstraint(OperatorType.BETWEEN, minNum, maxNum);
return handleMultiVersion(info, cellRangeAddressList, helper, constraint);
}
/**
* excel添加文本字符长度校验
*
* @param sheet 哪个 sheet 页添加校验
* @param minNum 最小值
* @param maxNum 最大值
* @param info 自定义提示
* @param col 第几列校验(0开始)
* @param maxRow 表头占用几行
* @return DataValidation
*/
private static DataValidation createTextLengthValidation(Sheet sheet, String minNum, String maxNum, String info, int col, int maxRow, int lastRow) {
// 1.设置验证
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(maxRow, lastRow, col, col);
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createTextLengthConstraint(OperatorType.BETWEEN, minNum, maxNum);
return handleMultiVersion(info, cellRangeAddressList, helper, constraint);
}
/**
* excel添加自定义校验
*
* @param sheet 哪个 sheet 页添加校验
* @param formula 表达式
* @param col 第几列校验(0开始)
* @param maxRow 表头占用几行
* @return DataValidation
*/
private static DataValidation createCustomValidation(Sheet sheet, String formula, String info, int col, int maxRow, int lastRow) {
String msg = "请输入正确的值!";
// 0.修正xls表达式不正确定位的问题,只修正了开始,如F3:F2000,修正了F3变为A2,F2000变为A2000
Workbook workbook = sheet.getWorkbook();
if (workbook instanceof HSSFWorkbook) {
int start = formula.indexOf("(") + 1;
int end = formula.indexOf(")");
if (start != 1 && end != 0) {
String prev = formula.substring(0, start);
String suffix = formula.substring(end);
String substring = formula.substring(start, end);
String[] ranges = substring.split(":");
StringBuilder chars = new StringBuilder();
Pattern pattern = Pattern.compile("([A-Z]+)(\\d+)");
for (String range : ranges) {
Matcher matcher = pattern.matcher(range);
if (matcher.find()) {
int rowNum = Integer.parseInt(matcher.group(2));
chars.append("A").append(rowNum - 1).append(":");
}
}
chars.deleteCharAt(chars.length() - 1);
formula = prev + chars + suffix;
}
}
// 1.设置验证
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(maxRow, lastRow, col, col);
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createCustomConstraint(formula);
DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
dataValidation.setEmptyCellAllowed(true);
dataValidation.setShowPromptBox(true);
if (info != null) {
msg = info;
}
dataValidation.createPromptBox("提示", msg);
return dataValidation;
}
}