使用poi实现Excel导入数据封装成实体类对象
实现流程:
1.创建一个springboot项目,创建简单项目就不演示了.
2.导入POI的依赖.
3.定义Excel注解.
4.创建ExcelUtils工具类.
5.创建ExcelController类.
6.创建需要封装的实体类.
7.实现思路:通过post请求将Excel文件发送到ExcelControlle接口, 然后调用ExcelUtils工具类的方法拿到解析好的 ArrayList<List<List<Object>>> list 集合对象 (外层集合存每页的数据 第二层集合存每页中行的数据 最内层集合存每一行中每一个单元个的数据),然后遍历集合将数据封装到自己定义好的实体类(实体类成员变量上加@Excel注解起个名用来判断对应Excel一行指定的名字)通过反射技术就可以实现数据封装.
具体步骤演示:
在pom.xml中导入依赖
<!--poi EXcel解析--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> </dependencies>
定义Excel注解
package com.example.excel.conf; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)//注解的生命周期 该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在; @Target(ElementType.FIELD)//注解的适用范围 FIELD代表用于描述域 public @interface Excel { /** * 导出到Excel中的名字. */ public String name(); /** * 日期格式, 如: yyyy-MM-dd */ public String dateFormat() default ""; /** * 读取内容转表达式 (如: 0=男,1=女,2=未知) */ public String readConverterExp() default ""; /** * 导出时在excel中每个列的高度 单位为字符 */ public double height() default 14; /** * 导出时在excel中每个列的宽 单位为字符 */ public double width() default 20; /** * 文字后缀,如% 90 变成90% */ public String suffix() default ""; /** * 当值为空时,字段的默认值 */ public String defaultValue() default ""; }
创建工具类
package com.example.excel.Util; import com.example.excel.conf.Excel; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.InputStream; import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import static org.apache.poi.ss.usermodel.CellType.BLANK; public class ExcelUtils { private final static String excel2003L =".xls"; //2003- 版本的excel private final static String excel2007U =".xlsx"; //2007版本 /** * @Description:获取IO流中的数据 * @param in,fileName * @return * @throws */ public static ArrayList<List<List<Object>>> getListByExcel(InputStream in, String fileName) throws Exception{ List<List<Object>> list = null; //创建Excel工作薄 Workbook work = getWorkbook(in,fileName); if(null == work){ throw new Exception("创建Excel工作薄为空!"); } Sheet sheet = null; //页数 Row row = null; //行数 Cell cell = null; //列数 ArrayList<List<List<Object>>> lists = new ArrayList<>(); //遍历Excel中所有的sheet for (int i = 0; i < work.getNumberOfSheets(); i++) { sheet = work.getSheetAt(i); if(sheet==null){continue;} list = new ArrayList<List<Object>>(); //遍历当前sheet中的所有行 for (int j = sheet.getFirstRowNum(); j <= sheet.getLastRowNum(); j++) { row = sheet.getRow(j); if (isRowEmpty(row)){ continue; } //if(row==null){continue;} //遍历所有的列 List<Object> li = new ArrayList<Object>(); for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) { cell = row.getCell(y); li.add(getValue(cell)); } list.add(li); } lists.add(list); } return lists; } //判断row是否为空 public static boolean isRowEmpty(Row row) { if (null == row) { return true; } int firstCellNum = row.getFirstCellNum(); //第一个列位置 int lastCellNum = row.getLastCellNum(); //最后一列位置 int nullCellNum = 0; //空列数量 for (int c = firstCellNum; c < lastCellNum; c++) { Cell cell = row.getCell(c); if (null == cell || BLANK == cell.getCellType()) { nullCellNum ++; continue; } /*cell.setCellType(STRING); String cellValue = cell.getStringCellValue().trim(); if (StringUtils.isEmpty(cellValue)) { nullCellNum ++; }*/ } //所有列都为空 if (nullCellNum == (lastCellNum - firstCellNum)) { return true; } return false; } /** * @Description:根据文件后缀,自适应上传文件的版本 * @param inStr,fileName * @return * @throws Exception */ public static Workbook getWorkbook(InputStream inStr, String fileName) throws Exception{ Workbook wb = null; String fileType = fileName.substring(fileName.lastIndexOf(".")); if(excel2003L.equals(fileType)){ wb = new HSSFWorkbook(inStr); //2003- }else if(excel2007U.equals(fileType)){ wb = new XSSFWorkbook(inStr); //2007+ }else{ throw new Exception("解析的文件格式有误!"); } return wb; } /** * @Description:对表格中数值进行格式化 * @param cell * @return */ //解决excel类型问题,获得数值 public static String getValue(Cell cell) { String value = ""; if(null==cell){ return value; } CellType cellType = cell.getCellType(); switch (cellType) { //数值型 case NUMERIC: if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { //如果是date类型则 ,获取该cell的date值 Date date = DateUtil.getJavaDate(cell.getNumericCellValue()); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); value = format.format(date);; }else {// 纯数字 BigDecimal big=new BigDecimal(cell.getNumericCellValue()); value = big.toString(); //解决1234.0 去掉后面的.0 if(null!=value&&!"".equals(value.trim())){ String[] item = value.split("[.]"); if(1<item.length&&"0".equals(item[1])){ value=item[0]; } } } break; //字符串类型 case STRING: value = cell.getStringCellValue(); break; // 公式类型 case FORMULA: //读公式计算值 value = String.valueOf(cell.getNumericCellValue()); if (value.equals("NaN")) {// 如果获取的数据值为非法值,则转换为获取字符串 value = cell.getStringCellValue(); } break; // 布尔类型 case BOOLEAN: value = " "+ cell.getBooleanCellValue(); break; default: value = cell.getStringCellValue(); } if("null".endsWith(value.trim())){ value=""; } return value; } /** * 用反射方法,获取列名,比较,为属性赋值 * @param object 实体类 * @param fieldName 列标题 * @param val 当前行当前列的值 */ public static void setFieldValueByFieldName(Object object, String fieldName, Object val) { try { //通过反射获取所有实体类所有定义的方法,object为数据库实体类 Field[] fields = object.getClass().getDeclaredFields(); //遍历fields for (int i = 0; i < fields.length; i++) { Field field = fields[i]; //读取注释,如@Excel(name = "科目") Excel annotation = field.getAnnotation(Excel.class); //因为private类型,所有要设置允许 field.setAccessible(true); //annotation.name() 注解里的属性,如果annotation没写注解,读取原始名字如subject if(annotation == null){ if(fieldName.equals(field.getName())){ if(field.getType() == Integer.class){ field.set(object, Integer.valueOf(val.toString())); }else if(field.getType() == Long.class){ field.set(object, Long.valueOf(val.toString())); }else { field.set(object, val); } return; } }else {//设置了注解,并且表格里的Excel字段值和注解的name值相同,则为相应字段赋值 if(fieldName.equals(annotation.name())){ //进行类型判断,因为实体类中变量类型不同。 if(field.getType() == Integer.class){ field.set(object, Integer.valueOf(val.toString())); }else if(field.getType() == Long.class){ field.set(object, Long.valueOf(val.toString())); }else if (field.getType() == Date.class){ Date date = DateUtil.parseYYYYMMDDDate(val.toString()); field.set(object,date); }else { field.set(object, val); } return; } } } }catch (Exception e){ e.printStackTrace(); } } /** * 解析导出值 0=男,1=女,2=未知 * * @param propertyValue 参数值 * @param converterExp 翻译注解 * @return 解析后值 * @throws Exception */ private static String convertByExp(String propertyValue, String converterExp) throws Exception { try { String[] convertSource = converterExp.split(","); for (String item : convertSource) { String[] itemArray = item.split("="); if (itemArray[0].equals(propertyValue)) { return itemArray[1]; } } } catch (Exception e) { throw e; } return propertyValue; } }
创建ExcelController类
package com.example.excel.controller; import com.example.excel.Util.ExcelUtils; import com.example.excel.pojo.Student; import com.example.excel.service.ExcelService; import org.apache.poi.ss.formula.functions.T; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/excel") public class ExcelController { @Resource ExcelService excelService; @PostMapping("/upload") public List<Student> upload(@RequestParam("file") MultipartFile file) { ArrayList<List<List<Object>>> list=null; try {
//获取输入流 InputStream inputStream = file.getInputStream();
//获取文件名 String fileName = file.getOriginalFilename(); //获取Excel中所有数据并封装成list集合 外层集合存每页的数据 第二层集合存每页中行的数据 最内层集合存每一行中每一个单元个的数据 list = ExcelUtils.getListByExcel(inputStream,fileName); List<Student> students = excelService.importCALoca(list); return students; }catch (Exception e){ } return null; } }
创建需要封装的实体类
package com.example.excel.pojo; import com.example.excel.conf.Excel; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class Student { @Excel(name = "姓名") private String name; @Excel(name = "年龄") private String age; @Excel(name = "年级") private String grade; }
创建ExcelService接口
package com.example.excel.service; import com.example.excel.pojo.Student; import java.util.ArrayList; import java.util.List; public interface ExcelService { List<Student> importCALoca(ArrayList<List<List<Object>>> list); }
创建ExcelService接口的package com.example.excel.service.impl;
import com.example.excel.Util.ExcelUtils; import com.example.excel.pojo.Student; import com.example.excel.service.ExcelService; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class ExcelServiceimpl implements ExcelService { @Override public List<Student> importCALoca(ArrayList<List<List<Object>>> list) { //最终的数据 List<Student> objList = new ArrayList<>(); List<List<Object>> firstRows = null; if(list != null && list.size() > 0){ for (int i = 0; i < list.size()-1; i++) { //获取每一页 firstRows = list.get(i); List<Object> list1=null; if (firstRows != null && firstRows.size() > 0) { //获取第一行 list1 = firstRows.get(0); } //遍历除第一行以外的Excel表格中的值 for (int a = 1; a < firstRows.size(); a++) { //rows是某一行,i = 1 为第二行, i = 2 为第三行 List<Object> rows = firstRows.get(a); Student student = new Student(); for (int j = 0; j < rows.size(); j++){ String cellVal = (String) rows.get(j);
//调用工具类利用反射技术封装实体类对象 ExcelUtils.setFieldValueByFieldName(student, list1.get(j).toString().trim(),cellVal); } objList.add(student); } } } return objList; } }
简单的代码实现封装就完事了,接下来看一下测试效果
新建一个Exce表格
用Postman进行测试,配置好访问路径和要传的文件
查看结果6个学生全部封装成功