接着上一篇,该如何做到bean的属性与excel的列相对应呢。。经过我一番百度学习,发现注解可以实现我的需求
附上大佬博客,基本就是跟着他写的,成功实现了功能
https://my.oschina.net/wang520/blog/894276
创建注解
package com.mqb.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* excel自定义注解,name为在实体字段excel对应的列名
* columnIndex为该字段对应在excel的列的索引,-1时不从excel获取值
* @author MQB
*
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
String name() default "";
int columnIndex() default -1;
}
测试用的bean
package com.mqb.test.poiTest;
import java.util.Date;
import org.springframework.stereotype.Component;
import com.mqb.annotation.Excel;
@Component
public class TTT {
@Excel(name="姓名",columnIndex=0)
String name;
@Excel(name="性别",columnIndex=1)
Boolean sex;
@Excel(name="出生日期",columnIndex=2)
Date birthday;
@Excel(name="数字",columnIndex=3)
Integer num;
public TTT() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getSex() {
return sex;
}
public void setSex(Boolean sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
@Override
public String toString() {
return "TTT [name=" + name + ", sex=" + sex + ", birthday=" + birthday + ", num=" + num + "]";
}
}
自定义工具类
package com.mqb.utils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import com.mqb.annotation.Excel;
import com.mqb.test.poiTest.TTT;
public class ExcelUtil {
public static String getValueByCellStyle(Cell cell) {
String value="";
switch(cell.getCellTypeEnum()) {
case STRING:
value=cell.getStringCellValue();
break;
case BOOLEAN:
value=String.valueOf(cell.getBooleanCellValue());
break;
case NUMERIC:
String str=cell.getCellStyle().getDataFormatString();
if("m/d/yy".equals(str)) {
value=cell.getDateCellValue().toString();
}else {
value=String.valueOf(cell.getNumericCellValue());
}
break;
default:
break;
}
return value;
}
public static <T> List<T> readExcel(Class<T> clazz,Workbook workbook) {
List<T> list=new ArrayList<>();
try {
for(int i=0;i<workbook.getNumberOfSheets();i++) {
Sheet sheet=workbook.getSheetAt(i);
for(int j=0;j<sheet.getPhysicalNumberOfRows();j++) {
if(j==0)//第一行默认为列名,跳过第一行
continue;
Row row=sheet.getRow(j);
T o=clazz.newInstance();//使用无参构造函数创建一个clazz类的实例
for(Field field:clazz.getDeclaredFields()) {
field.setAccessible(true);//可以访问私有字段
int cellnum=field.getAnnotation(Excel.class).columnIndex();
if(cellnum==-1)//-1表示该字段不用从excel中读取
continue;
System.out.println(field.getType()+"================");
//有新的需要的类型在此添加
if(field.getType().equals(String.class)) {
String value=getValueByCellStyle(row.getCell(cellnum));
field.set(o, value);
}else if(field.getType().equals(Boolean.class)) {
Boolean value=Boolean.valueOf(getValueByCellStyle(row.getCell(cellnum)));
field.set(o, value);
}else if(field.getType().equals(Integer.class)) {
String s=getValueByCellStyle(row.getCell(cellnum));
Integer value=Integer.parseInt(s.substring(0, s.indexOf(".")));
field.set(o, value);
}else if(field.getType().equals(Date.class)) {
Date value=new Date(getValueByCellStyle(row.getCell(cellnum)));
field.set(o, value);
}else {
System.out.println("缺少类型判断:"+field.getType());
return null;
}
}
list.add(o);
}
}
}catch(Exception e) {
System.out.println("出错了");
return null;
}
return list;
}
}
在上一篇博客中工具类的基础上添加了一个 readExcel 的方法。
具体思路就是:将workbook的每个sheet的每个row都遍历出来,每个sheet的第一个row跳过,这一行作为为列名行
当遍历到其他row时,通过反射获取到我们要封装的bean的实例
然后遍历bean的字段,读取该字段的Excel注解,获得该字段对应excel中列的索引 cellnum
通过 row.getCell()获取到对应的cell,传入getValueByCellStyle 做一些基本的处理,然后根据字段的类型将值修改然后赋值
这里我只进行了4个类型判断,有需要可以日后增加。
controller,这里我将 readExcel() 返回的list输出,可以看到成功封装
@RequestMapping(value="/testUpExcel",method=RequestMethod.POST)
@ResponseBody
public Msg uploadExcel(
MultipartFile uploadFile,
HttpServletRequest request) throws Exception, IOException {
//上传到本地磁盘
String realPath="E:\\upload\\temp\\excel\\";
//获取原始图片的拓展名-------暂时不判断后缀名
String originalFilename = uploadFile.getOriginalFilename();
//封装上传文件位置的全路径,就是硬盘路径+文件名
File targetFile = new File(realPath,originalFilename);
//使用MultipartFile接口中的方法,把本地文件上传到已经封装好的文件位置的全路径就是上面的targetFile
uploadFile.transferTo(targetFile);
FileInputStream stream=new FileInputStream(targetFile);
HSSFWorkbook workbook=new HSSFWorkbook(stream);
List<TTT> list=ExcelUtil.readExcel(TTT.class, workbook);
System.out.println("read excel completed,start out list:");
for(TTT t:list) {
System.out.println(t.toString());
}
return Msg.success();
}
我上传的excel
如果一定要求excel每列的顺序不能变,那可以对每个sheet的row0进行判断,
取得field的excel注解的columnIndex和name
对应row(columnIndex)的值是否和name一致