Java开发中如何用POI优雅的导入Excel文件

前言

接上一篇Java开发中如何用POI优雅的导出Excel文件. 本篇基于 注解 + 反射 实现Excel导入功能的实现。

导入相对导出略复杂,需要考虑数据类型的合理转换。

实现

这里我们通过表头校验实现列数据的匹配。

废话不多说,直接上代码。相关注释已经足够完善。

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
* @author 当我遇上你
* @公众号 当我遇上你
* @since 2020-05-27
*/
@Slf4j
public class ExcelUtils {

static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

/**
*
* @param multipartFile
* @param clz VO对象,对应Excel表头
* @param <T>
* @throws IOException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
public static <T> void importExcel(MultipartFile multipartFile, Class<T> clz) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if(null == multipartFile) {
throw new NullPointerException("请选择文件");
}

log.info(multipartFile.getName());

log.info("文件类型:{}", multipartFile.getContentType());
String fileName = multipartFile.getOriginalFilename();
log.info("文件名:{}", fileName);

if(!"application/vnd.ms-excel".equals(multipartFile.getContentType())) {
throw new RuntimeException("请选择正确的文件类型与文件!");
}

// 返回数据
List<T> list = new ArrayList<>();

InputStream inputStream = multipartFile.getInputStream();
Workbook wb = WorkbookFactory.create(inputStream);

// 读取第一个sheet
Sheet sheet = wb.getSheetAt(0);
// 获取最大行数(或者sheet.getLastRowNum())
int rownum = sheet.getPhysicalNumberOfRows();
// 反射获取字段
Field[] fields = clz.getDeclaredFields();
// 获取第一行(表头)
Row row = sheet.getRow(0);
// 获取最大列数
int column = row.getPhysicalNumberOfCells();

// 表头校验
for (int j = 0; j < fields.length; j++){
Field field = fields[j];
if (field.isAnnotationPresent(ExcelHeader.class)) {
ExcelHeader annotation = field.getAnnotation(ExcelHeader.class);
Cell cell = row.getCell(j);
if (cell == null || !getCellValue(cell).equals(annotation.value())) {
throw new RuntimeException("Excel格式错误");
}
}
}

// 处理行数据
for (int i = 1; i<rownum; i++) {

row = sheet.getRow(i);
// 遇到空行则结束
if (row == null) {
break;
}

T rowData = clz.getDeclaredConstructor().newInstance();

// 处理列数据
for (int j = 0; j < fields.length; j++){

Field field = fields[j];
// 设置属性可访问
field.setAccessible(true);

if (field.isAnnotationPresent(ExcelHeader.class)) {

ExcelHeader annotation = field.getAnnotation(ExcelHeader.class);

// 这里默认按列顺序,也可以根据columnIndex设置列顺序
int columnIndex = annotation.columnIndex();

Cell cell = row.getCell(j);
if (cell == null) {
continue;
}
// 获取列值
Object value = getCellValue(cell);
// 设置属性
setFieldValue(rowData, field, value);
}

}
list.add(rowData);
}
log.info("上传数据={}", list.toString());
}

private static <T> void setFieldValue(T rowData, Field field, Object value) throws IllegalAccessException {

if (field.getType() == int.class || field.getType() == Integer.class) {
field.set(rowData, value);
} else if (field.getType() == long.class || field.getType() == Long.class) {
field.set(rowData, value);
} else if (field.getType() == double.class || field.getType() == Double.class) {
field.set(rowData, value);
} else if (field.getType() == String.class) {
field.set(rowData, String.valueOf(value));
} else if (field.getType() == LocalDateTime.class) {
field.set(rowData, LocalDateTime.parse(String.valueOf(value), dateTimeFormatter));
}
}

private static Object getCellValue(Cell cell) {
CellType cellType = cell.getCellType();
Object cellValue = null;

if (cellType == CellType._NONE) {

} else if (cellType == CellType.NUMERIC) {
// 数值型
if (DateUtil.isCellDateFormatted(cell)) {
// 日期类型
Date d = cell.getDateCellValue();
cellValue = dateTimeFormatter.format(LocalDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault()));
} else {
double numericCellValue = cell.getNumericCellValue();
BigDecimal bdVal = new BigDecimal(numericCellValue);
if ((bdVal + ".0").equals(Double.toString(numericCellValue))) {
// 整型
cellValue = bdVal;
} else if (String.valueOf(numericCellValue).contains("E10")) {
// 科学记数法
cellValue = new BigDecimal(numericCellValue).toPlainString();
} else {
// 浮点型
cellValue = numericCellValue;
}
}
} else if (cellType == CellType.STRING) {
// 字符串型
cellValue = cell.getStringCellValue();
} else if (cellType == CellType.FORMULA) {
// 公式型
} else if (cellType == CellType.BLANK) {
// 空值
} else if (cellType == CellType.BOOLEAN) {
// 布尔型
cellValue = cell.getBooleanCellValue();
} else if (cellType == CellType.ERROR) {
// 错误
cellValue = cell.getErrorCellValue();
}

log.info("cellType={}, cellValue={}", cellType.name(), cellValue);
return cellValue;
}

}

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
@RestController
public class ExcelController {

/**
* http://localhost:8080/import
* @param request
* @return
*/
@PostMapping("/import")
public Object importExcel(HttpServletRequest request) throws IOException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
if (request instanceof MultipartHttpServletRequest) {
MultipartFile multipartFile = ((MultipartHttpServletRequest)request).getFile("file");
ExcelUtils.importExcel(multipartFile, User.class);
}
return "上传成功";
}
}

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】支持一下。