【java】通过配置导出excel
不定时更新一些自己写的代码
前言
写了挺多excel导出的代码了,这个算是自己比较满意的,记录下
一、配置表
T_EXPORT_EXCEL
ID | 导出excel模板ID |
---|---|
EXCEL_NAME | 导出excel的文件名 |
T_EXPORT_SHEET
ID | sheet页的ID |
---|---|
SHEET_NAME | sheet页的名称 |
EXCEL_ID | EXCEL表ID |
SORT | SHEET页在模板中的顺序 |
T_EXPORT_TAB
ID | 唯一主键 |
---|---|
HEAD | 表头 |
VALUE_EXPRESS | 取值表达式 |
VALUE_PARSER | 对取到的值进行封装 |
SHEET_ID | 关联的SHEET |
SORT | 单元格在表格中的顺序 |
二、Util类关键代码
导出接口:
/**
* excel导出
*
* @return
*/
public static HSSFWorkbook exportExcel(List<ExportTab> tabsCfg, List data, HSSFWorkbook wb, String sheetName) {
List<String[]> result = new ArrayList<>();
String[] heads = tabsCfg.stream().map(AntigenExportTab::getHead).collect(Collectors.toList()).toArray(new String[tabsCfg.size()]);
List<String> valueConfigs = tabsCfg.stream().map(AntigenExportTab::getValueExpress).collect(Collectors.toList());
List<String> valueParsers = tabsCfg.stream().map(AntigenExportTab::getValueParser).collect(Collectors.toList());
JSONArray array = JSONArray.parseArray(JSONArray.toJSONString(data));
for (int i = 0; i < array.size(); i++) {
// 单行数据最大拆分行数
int rowSpan = 1;
// 数据解析
Map<String, Object> valueMap = new HashMap<>();
for (String valueCfg : valueConfigs) {
if (valueCfg.startsWith("$")) {
Object val = getVal(array.getJSONObject(i), valueCfg.substring(1));
valueMap.put(valueCfg, val);
if (val instanceof List) {
int size = ((List) val).size();
rowSpan = rowSpan < size ? size : rowSpan;
}
}
}
// 数据生成
for (int row = 0; row < rowSpan; row++) {
String[] rowData = generateRowData(valueConfigs, row, valueMap, valueParsers);
if (rowData != null) {
if ("ROWNUM".equals(valueConfigs.get(0))) {
rowData[0] = String.valueOf(result.size() + 1);
}
result.add(rowData);
}
}
}
// 此处封装了调用POI将生成的sheet页插入到wb中,别人写的现成的,我就没贴上来
return ExportExcelUtil.getHSSFWorkbook(sheetName, heads, result.toArray(new String[result.size()][]), wb);
}
单行数据生成:
/**
* 生成一行数据
*
* @param valueConfigs
* @param row
* @param valueMap
* @param valueParsers
* @return
*/
private static String[] generateRowData(List<String> valueConfigs, int row, Map<String, Object> valueMap, List<String> valueParsers) {
String[] rowData = new String[valueConfigs.size()];
for (int cell = 0; cell < valueConfigs.size(); cell++) {
String valueKey = valueConfigs.get(cell);
String valueParser = valueParsers.get(cell);
Object value = valueMap.get(valueKey);
if (value instanceof List) {
List<String> vals = (List<String>) value;
if (vals.size() == 0) {
// 子集合为空时保证有一条信息
rowData[cell] = "";
} else if (vals.size() - 1 >= row) {
// 正常赋值
rowData[cell] = parsedExcelValue(vals.get(row), valueParser);
} else {
// 越界
return null;
}
} else {
rowData[cell] = parsedExcelValue((String) value, valueParser);
}
}
return rowData;
}
对取到的值进行封装:
private static String parsedExcelValue(String value, String parser) {
if (StringUtils.isEmpty(value) || StringUtils.isEmpty(parser)) {
return value;
}
try {
if (parser.startsWith("ArrayJSON_")) {
return arrayJsonParser(value, parser);
}
if (parser.startsWith("ArrayJSON0_")) {
return arrayJsonParser0(value, parser);
}
if (parser.startsWith("ArrayJSONS_")) {
return arrayJsonParserS(value, parser);
}
switch (parser) {
// 此处有一些一些业务上的特殊字段的处理,我删掉了
default:
return value;
}
} catch (Exception e) {
return value;
}
}
取数组的第N个字符串:
private static String arrayJsonParser(String value, String parser) {
Integer index = Integer.valueOf(parser.replace("ArrayJSON_", "")) - 1;
JSONArray array = JSONArray.parseArray(value);
return index >= 0 && array.size() > index ? array.getString(index) : "";
}
取数组的第1个元素中的对应value:
private static String arrayJsonParser0(String value, String parser) {
JSONArray array = JSONArray.parseArray(value);
return array.size() > 0 ? array.getJSONObject(0).getString(parser.replace("ArrayJSON0_", "")) : "";
}
取数组的中元素的parser对应value:
JSONArray array = JSONArray.parseArray(value);
if (array.isEmpty()) {
return "";
}
String format = parser.replace("ArrayJSONS_", "");
List<String> placeHolders = parses(format);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.size(); i++) {
JSONObject o = array.getJSONObject(i);
String str = format;
for (int j = 0; j < placeHolders.size(); j++) {
String placeHolder = placeHolders.get(j);
str = str.replace(placeHolder, o.getString(placeHolder.substring(1, placeHolder.length() - 1)));
}
if (sb.length() > 0) {
sb.append("\n");
}
sb.append(str);
}
return sb.toString();
上面调用到的子方法,做了个小缓存:
private static List<String> parses(String str) {
if (parsesMap.containsKey(str)) {
return parsesMap.get(str);
}
String pattern = "(\\{[\\s\\S]+?\\})";
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(str);
List<String> placeHolder = new ArrayList<>();
while (m.find()) {
placeHolder.add(m.group(0));
}
parsesMap.put(str, placeHolder);
return placeHolder;
}
private static Map<String, List<String>> parsesMap = new ConcurrentHashMap<>();
按表达式读取值:
private static Object getVal(JSONObject obj, String keyExpression) {
if (obj == null) {
return "";
}
int mapInd = keyExpression.indexOf('.');
int listInd = keyExpression.indexOf('-');
String temp = keyExpression.substring(1);
int mSub = temp.indexOf('.');
int lSub = temp.indexOf('-');
if (mSub == -1 && lSub == -1) {
if (obj.containsKey(temp)) {
return keyExpression.startsWith("-") ? obj.getJSONArray(temp).toJSONString()
: StringUtils.defaultIfBlank(obj.getString(temp), "");
} else {
return "";
}
}
int ind = getNextSplitInd(mSub, lSub);
String key = temp.substring(0, ind);
String expression = temp.substring(ind);
if (mapInd == 0) {
// map
JSONObject o = obj.getJSONObject(key);
return getVal(o, expression);
}
if (listInd == 0) {
// list
JSONArray array = obj.getJSONArray(key);
List<String> result = new ArrayList<>();
if (array == null) {
return result;
}
for (int i = 0; i < array.size(); i++) {
JSONObject o = array.getJSONObject(i);
Object val = getVal(o, expression);
if (val instanceof String) {
result.add((String) val);
}
}
return result;
}
return "";
}
下一个分隔符的位置:
private static int getNextSplitInd(int a, int b) {
if (a < 0 && b < 0) {
throw new BusinessException("数据异常");
} else if (a >= 0 && b >= 0) {
return a > b ? b : a;
} else {
return a > 0 ? a : b;
}
}
三、值表达式的配置
JSON如下(示例):
[
{
"className":"三年一班",
"chargeTeacher":{
"name":"孙老师",
"age":55,
"sampling":[
{
"site":"校门口",
"time":"202204014 12:00:00"
},
{
"site":"校门口",
"time":"202204013 12:00:00"
}
]
},
"students":[
{
"name":"张三",
"strongPoint":"[{\"point\":\"篮球\",\"awards\":\"市一等奖\"},{\"point\":\"乒乓球\",\"awards\":null}\"]",
"voluntary":"[\"清华\",\"北大\"]",
"sampling":[
{
"site":"校门口",
"time":"202204014 12:00:00"
},
{
"site":"校门口",
"time":"202204013 12:00:00"
}
]
},
{
"name":"李四"
}
]
},
{
"className":"三年二班",
"chargeTeacher":{
"name":"李老师",
"age":50
},
"students":[
{
"name":"王五"
}
]
}
]
配置如下(示例):
T_EXPORT_EXCEL
ID | EXCEL_NAME |
---|---|
1 | 班级信息导出 |
T_EXPORT_SHEET
ID | SHEET_NAME | EXCEL_ID | SORT |
---|---|---|---|
1 | 班主任信息 | 1 | 1 |
2 | 学生信息 | 1 | 2 |
T_EXPORT_TAB
ID | HEAD | VALUE_EXPRESS | VALUE_PARSER | SHEET_ID | SORT |
---|---|---|---|---|---|
11 | 班级 | $.className | 1 | 1 | |
12 | 班主任 | $.chargeTeacher.name | 1 | 2 | |
13 | 最新核酸采样时间 | $.chargeTeacher.sampling | ArrayJSON0_time | 1 | 3 |
21 | 班级 | $.className | 2 | 1 | |
22 | 姓名 | $-students.name | 2 | 2 | |
23 | 最新核酸采样时间 | $-students.sampling | ArrayJSON0_time | 2 | 3 |
24 | 第一志愿 | $-students.voluntary | ArrayJSON_1 | 2 | 4 |
25 | 第二志愿 | $-students.voluntary | ArrayJSON_2 | 2 | 5 |
26 | 特长 | $-students.strongPoint | ArrayJSONS_{point}:奖项{awards} | 2 | 6 |
总结
差不多了,记录下来万一哪天用得上过来ctrl+cv一下。