poi导出excel,支持2007和2003,支持javabean,利用反射对poi进行简单封装,支持任意实体和乱序的excel表格

需要的jar包有poi-3.17.jar
poi-ooxml-3.10-FINAL.jar
poi-ooxml-schemas-3.8.jar
xmlbeans-2.3.0.jar
/**
 * 将poi进行简单的封装,通过注解和反射,将excel中的集合和实体的set方法对应,形成执行集,再将执行集进行遍历,对bean进行封装,转化为
 * 主要支持任意实体,
 *  支持excel文件中字段的不规则排序
 *  单条记录的容错处理
 * 支持扩展类型转化
 * @author YKD
 *
 * @param <T>
 */
package gz.kd.utils.ExcelUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.hamcrest.core.IsInstanceOf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 将poi进行简单的封装,通过注解和反射,将excel中的集合和实体的set方法对应,形成执行集,再将执行集进行遍历,对bean进行封装,转化为
 * 主要支持任意实体,
 *  支持excel文件中字段的不规则排序
 * @author [email protected]   
 *
 * @param <T>
 */
public class ReadExcelByEntityUtils<T> {

	private Logger logger = LoggerFactory.getLogger(ReadExcelByEntityUtils.class); 
	
	private Workbook wb;//每一个Excel文件都将被解析成一个WorkBook对象
	
	private Sheet sheet;//Excel的每一页都将被解析成一个Sheet对象;
	
	private Row row;//Excel中的每一行都是一个Row对象,每一个单元格都是一个Cell对象。
	
	private Map<Integer,T> map; //最终结果集
  	
	private Class<T> entity;  //泛型类
	
	private Field[] fields;   //泛型方法集
	
	private List<Class> TypeList; //转化类型
	
	private Map<String,String> mapByAno=new HashMap<String,String>();//初始化集:<注解,属性名>

	/**
	 * 构造工具类
	 * @param filepath
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvalidFormatException 
	 */
	@SuppressWarnings("unchecked")
	public ReadExcelByEntityUtils(String filepath) throws InstantiationException, IllegalAccessException, InvalidFormatException{  
	        if(filepath==null){  
	        	System.out.println("Excel文件名为空");
	            return;  
	        }  
	        
	        String lastName = filepath.substring(filepath.lastIndexOf("."));  
	        try {  
	            InputStream is = new FileInputStream(filepath);  
	            if(".xls".equals(lastName)){  
	                wb = new HSSFWorkbook(is);  
	            }else if(".xlsx".equals(lastName)){  
	            	File file = new File(filepath);
	            	wb = WorkbookFactory.create(file);
	            }else{  
	                wb=null;  
	            } 
	         
	        } catch (FileNotFoundException e) {  
	            logger.error("文件找不到FileNotFoundException", e);  
	        } catch (IOException e) {  
	            logger.error("IOException", e);  
	        }  
	        entity = (Class<T>)((ParameterizedType)this.getClass().getGenericSuperclass())  
	                .getActualTypeArguments()[0];  
	        @SuppressWarnings("unused")
			T t = entity.newInstance();
	        if(this.entity!=null){
	        	 fields = entity.getDeclaredFields();//获取到属性字段
	        	 TypeList = new ArrayList<Class>();
	        	 for(Field f:fields){ 
	     			//设置强制访问
	     			f.setAccessible(true);
	     			EntiName e = f.getAnnotation(EntiName.class);
	     			if(!e.id()){                            //对true的字段进行拦截
	     				mapByAno.put(e.RName(),f.getName());
	     				TypeList.add(e.clazz());
	     			}
	        	 }
	        }
	    }  
	
	
	public Map<Integer,T> getMap() throws Exception{
		setEntityMap();
		return map;
	}
	
	/**
	 * 
	 * 将excel数据内容填充到map
	 * @throws Exception 
	 */
	private void setEntityMap() throws Exception{
		this.map = new HashMap<Integer, T>();  
		T t=null;
		String methodName=null;
		List<String> InvokeList = setInvokeList();
        sheet = wb.getSheetAt(0);  
        //总行数  
        int rowNum = sheet.getLastRowNum();  
        row = sheet.getRow(0);  
        //得到每一行单元格个数,包括其中的空单元格,这里要求表格内容的每一行都是固定的个数
        int colNum = row.getLastCellNum();  //每行单元格总数
        
       
        for (int i = 1; i <= rowNum; i++) {   //从第二行开始,遍历每一行
            row = sheet.getRow(i);                        
            t = exchangeEntity(InvokeList, colNum,i);  
            map.put(i-1, t);  //将封装好的实体放入map
        } 
	}


	private T exchangeEntity(List<String> InvokeList, int colNum,int rowNum){
		T t=null;
		try {
			DecimalFormat df = new DecimalFormat("#");          // 对长数字段进行string的转化
			String methodName;
			int j = 0;  
			t = entity.newInstance();  //每次新建一个T
			while (j < colNum) {  
			    Object obj = getCellFormatValue(row.getCell(j));  //
			    Class cs = TypeList.get(j);
			    methodName=InvokeList.get(j);
			    Method method = t.getClass().getMethod(methodName, TypeList.get(j));
			    if(obj==null||obj.equals("")){
			    	try {
						method.invoke(t, (Object)null); //Object转化,很关键
					} catch (Exception e) {
						// TODO Auto-generated catch block
						return t;
					} 
			    }else{
			    	Object cast;
			    	if(cs.getName().equals("java.lang.String")){
			    		//在这里拦截“excel中读取为double,date,int,boolean”,实际的实体函数形参却是String类型的
			    		if(obj.getClass().getName().equals("java.lang.Double")){
			    			cast = df.format(obj);
			            }else{
			            	cast = String.valueOf(obj);
			            }
			    			
			    	}else if(cs.getName().equals("java.lang.Integer")){//形参需要的类型
			    		if(obj.getClass().getName().equals("java.lang.Double")){
			    			String intNUm = df.format(obj);
			    			cast = Integer.valueOf(intNUm);
			            }else{
			            	 cast = cs.cast(obj); //可对类型进行扩展
			            }
			    	}/*else if(obj.getClass().getName().equals("java.util.Date")){
			    		
			    	}else if(obj.getClass().getName().equals("set方法中的类型")){
			    		类型转化处理
			    	}
			    	*/else{
			    		 cast = cs.cast(obj);
			    	}                                  
			    	//System.out.println(cast);
					method.invoke(t, cast);
			    }
			    j++;  
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("ExcelDEBUG:第"+rowNum+"行导入出错");
			return t;//有异常返回一个空t
		}
		return t;
	}
	
	
	
	
	/**
	 * 将标题头和实体的属性set方法对应上,形成一个执行函数集list,之后调用这个函数时,每次遍历list,然后invoke即可
	 * @throws Exception 
	 *
	 */
	private List<String> setInvokeList() throws Exception{
		if(wb==null){  
            throw new Exception("Workbook对象为空!");  
        }  
		List<String> invokeList = new ArrayList<String>();
		
		List<String> readExcelTitle = readExcelTitle();
		
		StringBuffer sb = new StringBuffer("set");
		//System.out.println("setInvokeList的判断"+readExcelTitle.size()+"?==?"+mapByAno.size());
		if(readExcelTitle.size()!=mapByAno.size()){
			System.out.println("KD:excel表头跟注解数量不对应");
			return null;
		}else{
			for (String obj : readExcelTitle) {
				if(mapByAno.get(obj)==null){
					System.out.println("KD:excel表头跟注解名称不对应");
					return null;
				}
				String fieldname = mapByAno.get(obj);
				mapByAno.remove(obj, fieldname);			//每拿一个put掉一个
				sb = new StringBuffer("set");
				String method = sb.append(fieldname.substring(0, 1).toUpperCase())
						.append(fieldname.substring(1)).toString();
				invokeList.add(method);
			}
			if(!mapByAno.isEmpty()){
				System.out.println("KD:excel表头跟注解名称不对应");
				return null;
			}
			return invokeList;
		/* */
		}
		
		
	}
	
	
	 /**
	  * 得到标题头,标题头一定要是string类型,否则报错
	  * @return
	  * @throws Exception
	  */
	 public List<String> readExcelTitle() throws Exception{
		 if(wb == null){
			 throw new Exception("KD:workbook对象为空");
		 }
		 sheet = wb.getSheetAt(0);
		 row = sheet.getRow(0);
		 //标题总列数
		int colNum = row.getPhysicalNumberOfCells();  //去掉对空列的计数
		 
		 //System.out.println("colNum:"+colNum);
		 List<String> list = new ArrayList<String>();
		 for(int i = 0;i<colNum;i++){
			 if(row.getCell(i)==null||row.getCell(i).equals("")){
				 list.add(null);
				 continue;
			 }else {
				 list.add((String) getCellFormatValue(row.getCell(i)));
			 }
		}
		 return list;
	 }
	 
	 @SuppressWarnings("deprecation")
	 private Object getCellFormatValue(Cell cell) {  
		 	DecimalFormat df = new DecimalFormat("#");          // 对长数字段进行string的转化
	        Object cellvalue = null;  
	        if (cell != null) {  
	            // 判断当前Cell的Type  
	            switch (cell.getCellType()) {  
	            case Cell.CELL_TYPE_NUMERIC:// 如果当前Cell的Type为NUMERIC  
	            case Cell.CELL_TYPE_FORMULA: {  
	                // 判断当前的cell是否为Date  
	                if (DateUtil.isCellDateFormatted(cell)) {  
	                    Date date = cell.getDateCellValue();  
	                    cellvalue = date;  
	                } else {// 如果是纯数字  
	                    // 取得当前Cell的数值  
	                    cellvalue = cell.getNumericCellValue();
	                    /*if(cellvalue.getClass().getName().equals("java.lang.Double")){
	                    	cellvalue = df.format(cellvalue);
	                    }如果在此处拦截double就会使得所有的double都变成string,而有的时候我并不需要转成string */
	                }  
	                break;  
	            }  
	            case Cell.CELL_TYPE_STRING:// 如果当前Cell的Type为STRING  
	                // 取得当前的Cell字符串  
	                cellvalue = cell.getRichStringCellValue().getString();  
	                break;  
	            default:// 默认的Cell值  
	                cellvalue = "";  
	            }  
	        } else {  
	            cellvalue = "";  
	        }  
	        return cellvalue;  
	    }  
	 
	
}
 
 
 
 
 
 
 
 
 
 
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 辅助ReadExcelByEntityUtils
 * @author 姚楷东 [email protected] 做人,要有激情
 */
@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)//指定注解在运行时有效
public @interface EntiName {
	
	 /** 
     * 是否为序列号 
     * @return 
     */  
    boolean id() default false;  
	/**
	 * 字段名称
	 * @return
	 */
	String RName()default "";
	/** 
     * 形参类型
     * @return 
     */  
    Class clazz()default String.class;
    
    
}
 
 
 
 
 
 
应用

举例


 
 
 
 
 
 
import java.util.Date;

import gz.kd.utils.ExcelUtils.EntiName;

public class Items {
	
	@EntiName(RName="序号",clazz=Integer.class)
    private Integer id;
	
	@EntiName(RName="名称")
    private String name;
	
	@EntiName(RName="价格",clazz=Double.class)
    private Double price;
	
	@EntiName(RName="照片",clazz=String.class)
    private String pic;
	
	@EntiName(RName="创建时间",clazz=Date.class)
    private Date createtime;
	
	@EntiName(RName="细节")
    private String detail;
  
  
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail == null ? null : detail.trim();
    }

	@Override
	public String toString() {
		return "Items [id=" + id + ", name=" + name + ", price=" + price + ", pic=" + pic + ", createtime=" + createtime
				+ ", detail=" + detail + "]";
	}
    
    
}
 
 
运行结果为封装好的Map实体
 
 
下面的表格,首行为字段,必需要与实体上的注解对应,顺序不需要对应;第二行开始为表格内容:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
名称	价格	照片	创建时间	                                细节
台式机		1a6f270c-714b-4070-b960-e25015615a19.jpg		细节
电脑	6000		2017/11/10 15:26	                        细节
背包	3600		2017/11/10 15:30	                      
背包			2017/11/10 15:30	                        细节1
 
 
 
 
 
 
 
 
如果不懂或者需要源码,留下评论,欢迎加qq 276368974 交流
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/weixin_36708538/article/details/78564908