日常开发过程中经常有需要上传Excel文档的需求,今天就给大家总结一篇比较好用的API接口。
1、首先引入jar
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
2、工具类伪代码:
import com.google.common.collect.Maps;
import com.nandao.demo.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
/**
* @author wanghuainan
* @date 2017/9/6 16:05
*/
@Slf4j
public class PoiUtil<T> {
Class<T> clazz;
/**
* 有参构造函数
* @param clazz
*/
public PoiUtil(Class<T> clazz) {
this.clazz = clazz;
}
public List<T> importExcel(Integer sheetIndex, MultipartFile file) throws Exception {
int maxCol = 0;
List<T> list = new ArrayList<T>();//定义范型的集合
Workbook workbook = null;
if (file.getOriginalFilename().endsWith("xls")) {
workbook = new HSSFWorkbook(file.getInputStream());
} else {
workbook = new XSSFWorkbook(file.getInputStream());
}
if (sheetIndex == null) {
sheetIndex = 0;
}
Sheet sheet = workbook.getSheetAt(sheetIndex);
int rows = sheet.getPhysicalNumberOfRows();
// 有数据时才处理
if (rows > 0) {
/**
* 通过反射取出实体的每个参数
*/
List<Field> allFields = getMappedFiled(clazz, null);
// 定义一个map用于存放列的序号和field.
Map<Integer, Field> fieldsMap = Maps.newHashMap();
Row titleRow = sheet.getRow(0);
for (Field field : allFields) {//遍历每个参数
// 将有注解的field存放到map中.
if (field.isAnnotationPresent(ExcelVOAttribute.class)) {//参数上是否有注解
/**
* 取出注解,并取出注解里面的参数,逐个匹配
*/
ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class);
// 获得列号
int col = getExcelCol(attr.column());
maxCol = Math.max(col, maxCol);
// 设置类的私有字段属性可访问.
field.setAccessible(true);
fieldsMap.put(col, field);
//校验标题必填列是否与模板一致
if (attr.required()) {
Cell cell = titleRow.getCell(col);
String title = null;
if (cell != null) {
title = cell.getStringCellValue();
}//校验标题和模版中是否一致
if (title == null || !title.trim().equals(attr.templateName())) {
throw new BizException(ResultCode.PARAM_ERROR.getCode(), "请检查导入模板,第" + (col + 1) + "列标题与模板不一致!");
}
}
}
}
// 从第2行开始取数据,默认第一行是表头.
for (int i = 1; i < rows; i++) {
Row row = sheet.getRow(i);
//判断空行
if (isEmptyRow(row)) {
break;
}
int cellNum = maxCol;
T entity = null;
for (int j = 0; j <= cellNum; j++) {
Cell cell = row.getCell(j);
Field field = fieldsMap.get(j);
ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class);
String filedName = attr.templateName();
Class<?> fieldType = field.getType();
if (cell == null) {
if (attr.required()) {
throw new BizException(ResultCode.PARAM_ERROR.getCode(), filedName + "不能为空,第" + (i + 1) + "行数据" + filedName + "为空!");
} else {
continue;
}
}
int cellType = cell.getCellType();
String c = "";
if (cellType == Cell.CELL_TYPE_NUMERIC) {
NumberFormat numberFormat = NumberFormat.getInstance();//防止数值转换成Doule小数点类型
// 不显示千位分割符,否则显示结果会变成类似1,234,567,890
numberFormat.setGroupingUsed(false);
if (String.valueOf(cell.getNumericCellValue()).indexOf("E") == -1) {
c = String.valueOf(numberFormat.format(cell.getNumericCellValue()));
} else {
if (String.valueOf(cell.getNumericCellValue()).indexOf("-") == -1) {
BigDecimal bd = new BigDecimal(String.valueOf(cell.getNumericCellValue()));
c = String.valueOf(bd.longValueExact());
} else {
BigDecimal bd = new BigDecimal(String.valueOf(cell.getNumericCellValue()));
c = bd.toString();
}
// c = String.valueOf(new DecimalFormat("#").format(cell.getNumericCellValue()));
}
} else if (cellType == Cell.CELL_TYPE_BOOLEAN) {
c = String.valueOf(cell.getBooleanCellValue()).trim();
} else {
c = cell.getStringCellValue().trim();
}
if (c == null || StringUtils.isEmpty(c)) {
if (attr.required()) {
throw new BizException(ResultCode.PARAM_ERROR.getCode(), filedName + "不能为空,第" + (i + 1) + "行数据" + filedName + "为空!");
} else {
continue;
}
}
try {
entity = (entity == null ? clazz.newInstance() : entity);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (field == null) {
continue;
}
try {
// 取得类型,并根据对象类型设置值.
if (String.class == fieldType) {
field.set(entity, c);
} else if ((Integer.TYPE == fieldType)
|| (Integer.class == fieldType)) {
field.set(entity, Integer.valueOf(c));
} else if ((Long.TYPE == fieldType)
|| (Long.class == fieldType)) {
field.set(entity, Long.valueOf(c));
} else if ((Float.TYPE == fieldType)
|| (Float.class == fieldType)) {
field.set(entity, Float.valueOf(c));
} else if ((Short.TYPE == fieldType)
|| (Short.class == fieldType)) {
field.set(entity, Short.valueOf(c));
} else if ((Double.TYPE == fieldType)
|| (Double.class == fieldType)) {
field.set(entity, Double.valueOf(c));
} else if (Character.TYPE == fieldType) {
if ((c != null) && (c.length() > 0)) {
field.set(entity, Character
.valueOf(c.charAt(0)));
}
}
} catch (Exception e) {
e.printStackTrace();
throw new BizException(ResultCode.PARAM_ERROR.getCode(), "第" + (i + 1) + "行数据" + filedName + "格式非法,请检查EXCEL");
}
}
if (entity != null) {
list.add(entity);
}
}
}
return list;
}
}
通过反射取参数的代码:
private List<Field> getMappedFiled(Class clazz, List<Field> fields) {
if (fields == null) {
fields = new ArrayList<Field>();
}
Field[] allFields = clazz.getDeclaredFields();// 得到所有定义字段
// 得到所有field并存放到一个list中.
for (Field field : allFields) {
if (field.isAnnotationPresent(ExcelVOAttribute.class)) {
fields.add(field);
}
}
if (clazz.getSuperclass() != null
&& !clazz.getSuperclass().equals(Object.class)) {
getMappedFiled(clazz.getSuperclass(), fields);
}
return fields;
}
自定义注解:
/**
* @author wanghuainan
* @date 2017/9/6 16:10
*/
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target( { java.lang.annotation.ElementType.FIELD })
public @interface ExcelVOAttribute {
/**
* 导出到Excel中的名字.
*/
String name() default "";
/**
* 下载模板中的名字.
*/
String templateName() default "";
/**
* 配置列的名称,对应A,B,C,D....
*/
String column();
boolean required() default false;
/**
* 导出到Excel中的宽度.
*/
String width() default "";
/**
* 提示信息
*/
String prompt() default "";
/**
* 设置只能选择不能输入的列内容.
*/
String[] combo() default {};
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
boolean isExport() default true;
}
3、控制层的代码:
/**
* 上传excel表格
* @param file
* @return
*/
@RequestMapping("uploadExcel")
public R uploadExcel(@RequestParam("file") MultipartFile file){
/**
* 通过有参构造函数创建工具类
*/
PoiUtil<UserInfoVoTemplate> userVoTemplatePoi = new PoiUtil(UserInfoVoTemplate.class);
List<UserInfoVoTemplate> userVoTemplateExcels = null;
List<Long> userIdList = new ArrayList<>();
try {
/**
* 正式取出excel中的内容生成list集合
*/
userVoTemplateExcels = userVoTemplatePoi.importExcel(0, file);
/**
* 通过流式变成取某个参数和成集合
*/
userIdList = userVoTemplateExcels.stream().map(u ->u.getUserId()).collect(Collectors.toList());
log.info("上传的用户id:[{}]",userIdList.toString());
} catch (Exception e) {
e.printStackTrace();
}
return R.ok();
}
定义的对象:
package com.nandao.demo.entity;
import com.nandao.demo.common.ExcelVOAttribute;
import lombok.Data;
import java.io.Serializable;
/**
* @author wanghuainan
* @date 2017/9/06
*/
@Data
public class UserInfoVoTemplate implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户id
*/
@ExcelVOAttribute(templateName = "用户主键", name = "用户主键", column = "A", width = "4000", required = true)
private Long userId;
}
集成结束,然后通过postman,就可以测试了,后期我们还会分享一篇关于生成Excel文档的文章,敬请期待!