1、开篇
在开发中总是有需求是要导出某些系统数据以及向系统中批量获取数据,为了快速开发这些功能,减少做重复的工作,特此封装成了常用工具。
---需要引入的模块---
<!--RabbitMQ依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.10</version>
</dependency>
2、数据导出模块
实现思路利用反射做到注解标识字段的汇聚,将需要导出的字段进行标记,这里用到mybatis的注解。当传入数据为空数据集合的时候可以当做模板导出。
public static void createExcel(HttpServletRequest request, HttpServletResponse response, Class clazz, List<?> list, String fileName){
ExcelWriter writer = ExcelUtil.getWriter();
// 获取当前类字段
Field[] fields = clazz.getDeclaredFields();
// 字段名称集合
List < String > fieldNames = new ArrayList< >();
// 字段中文名称集合(获取实体中@ApiModelProperty注解value的值)
List < String > cnNames = new ArrayList< >();
String mimeType = request.getServletContext().getMimeType(fileName);
if (mimeType != null && !"".equals(mimeType)) {
response.setContentType(mimeType);
}
for(Field field: fields) {
if(!field.isAccessible()) {
// 关闭反射访问安全检查,为了提高速度
field.setAccessible(true);
}
String fieldName = field.getName();
// 判断是否有@ApiModelProperty注解
boolean annotationPresent = field.isAnnotationPresent(ApiModelProperty.class);
if(annotationPresent && !"deFlag".equals(fieldName) && !"createUser".equals(fieldName)
&& !"updateUser".equals(fieldName) && !"updateTime".equals(fieldName)) {
ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
fieldNames.add(fieldName);
String name = annotation.value();
cnNames.add(name);
} else {
//排除字段操作(如果为true,则不设置alias的字段将不被输出)
writer.setOnlyAlias(true);
}
}
String[] fs = fieldNames.toArray(new String[0]);
String[] ns = cnNames.toArray(new String[0]);
for(int i = 0; i < ns.length; i++) {
// 设置表头及字段名
writer.addHeaderAlias(fs[i], ns[i]);
}
// 自动换行
Workbook workbook = writer.getWorkbook();
StyleSet styleSet = new StyleSet(workbook);
styleSet.setWrapText();
writer.setStyleSet(styleSet);
writer.write(list, true);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()){
for(int i = 0; i < fieldNames.size(); i++) {
writer.setColumnWidth(i, 23);
}
writer.flush(baos, true);
writer.close();
StaticFileUtils.loadToStream(new ByteArrayInputStream(baos.toByteArray()),request,response,fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
3、数据导入
将特定的数据导入,我们所知excel表格的表头肯定是中文,而我们实体类基本上使用英文来标记的这怎么让他们一一对应呢,没错还是用反射,根据注解上的中文绑定英文来进行反射写入数据。
public List<T> importExcel(MultipartFile multipartFile, Class clazz) throws Exception{
Workbook workbook = null;
//实例对象
List<T> list = new ArrayList<>();
try (InputStream inputStream = multipartFile.getInputStream()) {
String fileName = multipartFile.getOriginalFilename();
if (fileName.endsWith(".xls")) {
workbook = new HSSFWorkbook(inputStream);
} else {
//不是导入模板
return null;
}
} catch (IOException e) {
e.printStackTrace();
//不是导入模板
return null;
}
Sheet sheet = workbook.getSheetAt(0);
//获取总行数
int rows = sheet.getPhysicalNumberOfRows();
if (rows < 2) {
//没有数据
return null;
}
//获取所有字段名
Field[] fields = clazz.getDeclaredFields();
//记录对应的中文和英文
Map<String, String> cMap = new HashMap<>();
Map<String, Map<String, Class<?>>> countMap = new HashMap<>(fields.length);
for (Field field : fields) {
if (!field.isAccessible()) {
// 关闭反射访问安全检查,为了提高速度
field.setAccessible(true);
}
String fieldName = field.getName();
// 排除ID和序号
if (!"deFlag".equals(fieldName) && !"serialVersionUID".equals(fieldName) && !"createUser".equals(fieldName)
&& !"updateUser".equals(fieldName) && !"updateTime".equals(fieldName) && !"logId".equals(fieldName)) {
// 判断是否有@ApiModelProperty注解
boolean annotationPresent = field.isAnnotationPresent(ApiModelProperty.class);
if (annotationPresent && !"deFlag".equals(fieldName) && !"createUser".equals(fieldName)
&& !"updateUser".equals(fieldName) && !"updateTime".equals(fieldName)) {
ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
String name = annotation.value();
Class<?> type = field.getType();
Map<String, Class<?>> inMap = new HashMap<>();
inMap.put(fieldName, type);
countMap.put(name, inMap);
cMap.put(name, fieldName);
}
}
}
//获取表头
Row rowHead = sheet.getRow(0);
int headNum = rowHead.getPhysicalNumberOfCells();
//遍历总条数
for (int c = 1; c < rows; c++) {
T obj = (T) clazz.newInstance();
//获取该行数据
Row rowDate = sheet.getRow(c);
//根据头写入数据
for (int h = 0; h < headNum; h++) {
Cell cell = rowDate.getCell(h);
if(cell != null) {
//获取实体对应方法
String typeName = rowHead.getCell(h).getStringCellValue();
Map<String, Class<?>> inMap = countMap.get(typeName);
String typeEName = cMap.get(typeName);
String typeztName = inMap.get(typeEName).getName();
Method method = obj.getClass().getMethod("set" + StringUtil.initcap(typeEName), inMap.get(typeEName));
//根据对象的不同属性字段转换单元格中的数据类型并调用 set 方法赋值
if ("java.lang.Integer".equals(typeztName) || "int".equals(typeztName)) {
Integer param = cell.getColumnIndex();
method.invoke(obj, param);
} else if ("java.lang.Long".equals(typeztName)) {
cell.setCellType(CellType.STRING);
Long param = Long.valueOf(cell.getStringCellValue());
method.invoke(obj, param);
}else if ("java.lang.Double".equals(typeztName)) {
cell.setCellType(CellType.STRING);
Double param = Double.valueOf(cell.getStringCellValue());
method.invoke(obj, param);
}else if ("java.util.Date".equals(typeztName)) {
Date param = cell.getDateCellValue();
method.invoke(obj, param);
} else if ("java.time.LocalDateTime".equals(typeztName)) {
LocalDateTime param = cell.getLocalDateTimeCellValue();
// String pattern = "yyyy-MM-dd HH:mm:ss";
method.invoke(obj, param);
} else if ("java.lang.String".equals(typeztName)) {
cell.setCellType(CellType.STRING);
String param = cell.getStringCellValue();
method.invoke(obj, param);
} else {
//不是上述类型
}
}
}
//将实体写入集合
list.add(obj);
}
return list;
}
以上是主要代码部分和设计思路