日常开发过程中经常有需要上传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文档的文章,敬请期待!