Java处理导入excel文件的解决思路和方案

Java处理Excel导入的解决思路和方案

 

我们要处理导入的excel数据,这个是典型的信息技术处理。

所以按照惯有模式分为如下三步:

1.将数据录入到计算机

2.校验和格式化数据

3.处理这些数据

 

注意步骤一和步骤2,3可以同时进行的

我们将步骤一的输出 抽象出对应的数据结构,然后步骤2和3直接使用这些数据结构处理就可以了.

 

 

数据录入到计算机这个过程  方法比较多,也是处理的技术难点细节

我们可以扫描录入,使用流来读取文件等等一系列方法。

当前我们处理的是excel文件,因此要采用流来处理

 

读取excel的方法

和excel打交道肯定少不了这个POI组件了

我们要这个POI的jar包

Workbook ,Sheet , Row , Cell  这几个就是基本的处理对象

 

1.创建 Workbook 

  使用 其构造函数即可

 

    public HSSFWorkbook(InputStream s) throws IOException {
        this(s,true);
    }

 

 

 

2.得到sheet

 

    public static Sheet getSheet(Workbook work,int sheetIndex) throws Exception{
        Sheet sheet = null;
        if(work.getNumberOfSheets() >= 0){
            sheet = work.getSheetAt(sheetIndex);
        }
        return sheet;
    }

 

 

3.得到Row

 

    public static Row getRow(Sheet sheet,int rowIndex) throws Exception{
        return sheet.getRow(rowIndex);
    }

 

 

 

4.得到Cell

使用  Row的  迭代器模式  

Iterator<Cell> cellIterator(); 

因此可以如下访问:

 

for (Cell cell : row) {
}


或者使用

 

 

int getRowNum();
Cell getCell(int cellnum);


来进行也可以

 

 

通过上面的方法我们就可以定位到一个excel的具体的一个sheet和具体的一行的一个单元格了


 

将excel读取到的List数据放入到我们设计的数据结构中

 

 

接下来就是要将读取的excel数据放入到我们设计的数据结构中了

这里我们可以使用  注解+bean的方式  或者 将字段属性配置在xml中也可以

 

这里使用  注解的方式来进行.

 

考虑到放入的数据要  分类型 , 校验 , 格式化等

因此我们的注解可以从上面几个方面进行入手

 

 

1.创建注解

 

 

 

例如:

 

@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelField {

    /**
     * 属性的标题名称
     */
    String title();

    /**
     * 在excel的顺
     */
    int colum() default 9999;
    
    Class claz();
    
    int maxLength() ;

}

 

 

2.解析注解方法

创建的注解将来标记我们对应的 数据vo中的字段

因此我们还需要将单元格的数据和vo中的字段类型进行绑定,因此需要一个解析注解的工具

我们输入的是一个bean的class对象

然后使用反射来解析这个class,注意我们加入了父类的处理

 

        List<Field> fields = new ArrayList<>();
        for (Class clazz = clz; clazz != Object.class; clazz = clazz.getSuperclass()) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        }
        for (Field field : fields) {
            // 是否使用ExcelField注解
            if (field.isAnnotationPresent(ExcelField.class)) {
                ExcelField er = field.getAnnotation(ExcelField.class);
            }
        }

 

 

 

3.优化

 


通过上面的代码我们可以得到一个Field对应的注解

但是为了后续处理方便,我们在将数据存入到我们设计的数据结构中的时候我们其实还需要反射来获取这个  对象的某一个field,然后再获取它的注解

然后再使用注解中的colum的值和  excel列的colum的值比对,对应的值放入到对应的 field中

为了简化这个步骤,我们上面的处理可以多加一个 处理。

注意到excel每一行的值其实呢就是表头的对应的字段名的值

因此我们可以模拟一个表头ExcelHeader

这个类里面有  

 

public class ExcelHeader implements Comparable<ExcelHeader> {
    /**
     * excel的标题名称
     */
    private String title;
    /**
     * 每一个标题的顺
     */
    private int order;
    /**
     * 字段变量名
     */
    private String filed;
}

 

 

 

这样我们可以确定一个字段在第几个位置。

解析的时候我们解析  表头第一行的标题 然后可以得到一个  Map<Integer, ExcelHeader> ,也就是通过excel中单元格所在的列的号

来得到这个单元格的数据的变量名。

 

当我们处理其余数据行的时候,通过当前单元格的列号从上面的map中取得对应的变量名然后我们使用反射将单元格的值给塞到bean中属性为变量名的字段中.

这样做比我们现场再进行  bean的解析会避免掉多层的循环处理。

如果不这样做我们  还要在循环单元格的内部里面  再次循环  bean的fields然后做这个解析,效率就会很慢的。

 

可见  Map是一个  去掉循环层次的好方法。如果我们嵌套的循环只是为了去寻找某个值的时候,可以将嵌套循环中对应的集合的数据存入到Map中

然后我们就可以削减掉循环层次 。 典型的空间换时间方法.

 

处理得到的数据

经过上面的过程,数据已经放入到了我们的设计的数据结构中了

我们可以继续增加更加共通的处理方法。

因为注解中含有校验的信息和格式化的信息。

这种做法前面 介绍注解的使用的文章有过介绍。可以参考.

 

下面是上面涉及到的代码的文件:

ExcelField.java   ExcelHeader.java  ImportExcelHeaderUtil.java    ImportExcelUtil.java

 

1. ExcelField.java

 

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelField {

    /**
     * 属性的标题名称
     */
    String title();

    /**
     * 在excel的顺
     */
    int colum() default 9999;
    
    Class claz();
    
    int maxLength() ;

}

 

 

 

 

 

 

2. ExcelHeader.java

 

public class ExcelHeader implements Comparable<ExcelHeader> {
    /**
     * excel的标题名称
     */
    private String title;
    /**
     * 每一个标题的顺
     */
    private int order;
    /**
     * 变量名
     */
    private String filed;
    

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public String getFiled() {
        return filed;
    }

    public void setFiled(String filed) {
        this.filed = filed;
    }


    public int compareTo(ExcelHeader o) {
        return order - o.order;
    }

    public ExcelHeader(String title, int order, String filed) {
        super();
        this.title = title;
        this.order = order;
        this.filed = filed;
        
    }

}

 

 

 

 

 

3.  ImportExcelHeaderUtil.java

 

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;

import java.lang.reflect.Field;
import java.util.*;


public class ImportExcelHeaderUtil {

    /**
     * <p>根据JAVA对象注解获取Excel表头信息</p></br>
     */
    public static List<ExcelHeader> getHeaderList(Class clz) {
        List<ExcelHeader> headers = new ArrayList<>();
        List<Field> fields = new ArrayList<>();
        for (Class clazz = clz; clazz != Object.class; clazz = clazz.getSuperclass()) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        }
        for (Field field : fields) {
            // 是否使用ExcelField注解
            if (field.isAnnotationPresent(ExcelField.class)) {
                ExcelField er = field.getAnnotation(ExcelField.class);
                headers.add(new ExcelHeader(er.title(), er.colum(), field.getName()));
            }
        }
        Collections.sort(headers);
        return headers;
    }

    public static Map<Integer, ExcelHeader> getHeaderMap(Row titleRow, Class clz) {
        List<ExcelHeader> headers = getHeaderList(clz);
        Map<Integer, ExcelHeader> maps = new HashMap<>();
        for (Cell c : titleRow) {
            String title = c.getStringCellValue();
            for (ExcelHeader eh : headers) {
                if (eh.getTitle().equals(title.trim())) {
                    maps.put(c.getColumnIndex(), eh);
                    break;
                }
            }
        }
        return maps;
    }
}

 

 

 

4.ImportExcelUtil.java

 

import org.apache.commons.beanutils.BeanUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ImportExcelUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImportExcelUtil.class);

    private final static String excel2003L =".xls";    //2003- 版本的excel
    private final static String excel2007U =".xlsx";   //2007+ 版本的excel



    /**
     * 描述:获取IO流中的数据,组装成List<T>对象
     *      获取第0个sheet中,从0行到最后一行的数据,默认第0行为title
     * @param in    导入文件转换的流
     * @param fileName 文件名称
     * @param beanClass 转换成beanClass
     * @return
     * @throws IOException
     */
    public static <T> List<T> getDefaultDataListByExcel(InputStream in,String fileName,Class<T> beanClass) throws Exception{
        return getDataListByExcel(in,fileName,beanClass,0,0,Integer.MAX_VALUE);
    }



    /**
     * 描述:获取IO流中的数据,组装成List<T>对象
     *      获取第sheetIndex个sheet中,从0行到最后一行
     * @param in    导入文件转换的流
     * @param fileName 文件名称
     * @param beanClass 转换成beanClass
     * @param sheetIndex 要获取的sheet的index值
     * @return
     * @throws IOException
     */
    public static <T> List<T> getFullDataListByExcel(InputStream in,String fileName,Class<T> beanClass,int sheetIndex) throws Exception{
        return getDataListByExcel(in,fileName,beanClass,sheetIndex,0,Integer.MAX_VALUE);
    }

    /**
     * 描述:获取IO流中的数据,组装成List<T>对象
     * @param in    导入文件转换的流
     * @param fileName 文件名称
     * @param beanClass 转换成beanClass
     * @param sheetIndex 要获取的sheet的index值
     * @return
     * @throws IOException
     */
    public static <T> List<T> getDataListByExcel(InputStream in,String fileName,Class<T> beanClass,int sheetIndex,int beginRowNum,int endRownum) throws Exception{
        //创建Excel工作薄
        Workbook work = getWorkbook(in,fileName);
        if(null == work){
            throw new Exception("创建Excel工作薄为空!");
        }
        //获取响应的sheet
        Sheet sheet = getSheet(work,sheetIndex);
        //获取sheet的表头
        Row titleRow = getRow(sheet,beginRowNum);
        //返回的list<T>结果
        List<T> list = getPartDataListByExcel(titleRow,sheet,beanClass,beginRowNum,endRownum);
        work.close();
        return list;
    }


    public static <T> List<T> getPartDataListByExcel(Row titleRow,Sheet sheet,Class<T> beanClass,int beginRowNum,int endRownum) throws Exception{
        //返回的list<T>结果
        List<T> list = new ArrayList<T>();
        //获取导入文件对应的bean的标题map
        Map<Integer, ExcelHeader> maps = ImportExcelHeaderUtil.getHeaderMap(titleRow, beanClass);
        //遍历当前sheet中的所有行
        for (int j = beginRowNum+1; j <= endRownum; j++) {
            Row row = sheet.getRow(j);
            //新建一个T类
            T bean = beanClass.newInstance();
            //遍历所有的列
            for (Cell cell : row) {
                int ci = cell.getColumnIndex();
                ExcelHeader header = maps.get(ci);
                if (null == header)
                    continue;
                String filed = header.getFiled();
                Object value = getCellValue(cell);
                BeanUtils.copyProperty(bean, filed, value);
            }
            list.add(bean);
        }
        //work.close();
        return list;
    }



    /**
     * 描述:根据sheetIndex,得到响应的sheet
     * @param work,fileName
     * @return Sheet
     * @throws Exception
     */
    public static Sheet getSheet(Workbook work,int sheetIndex) throws Exception{
        Sheet sheet = null;
        if(work.getNumberOfSheets() >= 0){
            sheet = work.getSheetAt(sheetIndex);
        }
        return sheet;
    }

    /**
     * 描述:根据rowIndex,得到响应的row
     * @param sheet,rowIndex
     * @return Sheet
     * @throws Exception
     */
    public static Row getRow(Sheet sheet,int rowIndex) throws Exception{
        return sheet.getRow(rowIndex);
    }


    /**
     * 描述:根据文件后缀,自适应上传文件的版本
     * @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;
    }


    /**
     * 描述:对表格中数值进行格式化
     * @param cell
     * @return
     */
    private static Object getCellValue(Cell cell){
        Object value = null;
        DecimalFormat df = new DecimalFormat("0");  //格式化number String字符
        SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");  //日期格式化
        DecimalFormat df2 = new DecimalFormat("0.00");  //格式化数字

        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_STRING://字符串型
                value = cell.getRichStringCellValue().getString();
                break;
            case Cell.CELL_TYPE_NUMERIC://日期或数字
                if("General".equals(cell.getCellStyle().getDataFormatString())){
                    value = df.format(cell.getNumericCellValue());
                }else if("m/d/yy".equals(cell.getCellStyle().getDataFormatString())){
                    value = sdf.format(cell.getDateCellValue());
                }else{
                    value = df2.format(cell.getNumericCellValue());
                }
                break;
            case Cell.CELL_TYPE_BOOLEAN://布尔型
                value = cell.getBooleanCellValue();
                break;
            case Cell.CELL_TYPE_BLANK://空字符串
                value = "";
                break;
            case Cell.CELL_TYPE_FORMULA://公式
                CellStyle style = cell.getCellStyle();

                value = cell.getNumericCellValue();
                break;
            default:
                break;
        }
        return value;
    }


    public static void main(String[] args) throws FileNotFoundException {
        String url = "/usr/kaifeng/test.xlsx";
        File file = new File(url);
        FileInputStream fileInputStream = new FileInputStream(file);
        try {
            List<DataVo> list = getDataListByExcel(fileInputStream,"redeem.xlsx", DataVo.class,0,0,2);

        } catch (Exception e) {
            e.printStackTrace();
        }


    }


}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值