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

来源:互联网 发布:风水罗盘软件 编辑:程序博客网 时间:2024/06/07 01:47
需要的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 姚楷东 276368974@qq.com    * * @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();  //每次新建一个Twhile (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 blockreturn 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 blocke.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 姚楷东 276368974@qq.com 做人,要有激情 */@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();    }@Overridepublic String toString() {return "Items [id=" + id + ", name=" + name + ", price=" + price + ", pic=" + pic + ", createtime=" + createtime+ ", detail=" + detail + "]";}        }
运行结果为封装好的Map实体
下面的表格,首行为字段,必需要与实体上的注解对应,顺序不需要对应;第二行开始为表格内容:
名称价格照片创建时间                                细节台式机1a6f270c-714b-4070-b960-e25015615a19.jpg细节电脑60002017/11/10 15:26                        细节背包36002017/11/10 15:30                      背包2017/11/10 15:30                        细节1
如果不懂或者需要源码,留下评论,欢迎加qq 276368974 交流
 
阅读全文
1 0
原创粉丝点击