POI工具操作excel(七)Excel文件导入

       前面两篇讲的是excel的导出,在系统中,也会出现把excel表格中的数据导入到数据库的操作。那么这篇就简单的封装一个基于注解的工具类,可以利用POI技术方便的读取excel文件中的数据,把每一行数据封装成了一个我们的Java对象,然后存入到集合中。拿到集合数据就可以使用其他的持久化技术,把数据存储到数据库中了。

1、环境准备

本次使用的工程还是之前那篇文章的工程,具体创建请参照之。

准备一个excel的文件,内容如下:

2、自定义注解

       自定义注解:@ExcelAnnotation,该注解只定义了一个sort属性,它的作用是:Java类的属性和excel文件中的列的属性一一对应。

package com.poi.imports.annotation;

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelAnnotation {

    int sort();//Java类的属性对应的列
}

3、定义Person类,添加注解

Person类的属性和excel文件中的列一一对应;

package com.poi.imports.pojo;

import com.poi.imports.annotation.ExcelAnnotation;

import java.util.Date;

public class Person {
    @ExcelAnnotation(sort = 1)
    private String pid;
    @ExcelAnnotation(sort = 2)
    private String name;
    @ExcelAnnotation(sort = 3)
    private String role;
    @ExcelAnnotation(sort = 4)
    private String job;
    @ExcelAnnotation(sort = 5)
    private String hobby;
    @ExcelAnnotation(sort = 6)
    private Date birthday;
    @ExcelAnnotation(sort = 7)
    private double money;

    public Person() {//无参构造不能缺少
    }

    public Person(String pid, String name, String role, String job, String hobby, Date birthday, double money) {
        this.pid = pid;
        this.name = name;
        this.role = role;
        this.job = job;
        this.hobby = hobby;
        this.birthday = birthday;
        this.money = money;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

 

4、构建工具类

package com.poi.imports.util;

import com.poi.imports.annotation.ExcelAnnotation;
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.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * excel文件导入,解析成对象的集合的工具类
 * 该类的作用:把excel文件中的数据,每一行封装成一个对象,然后把对象封装成一个List集合
 */
public class ExcelImportUtil<T> {
    private Class<T> clazz;//对应的对象的CLass对象
    private Field[] fields;

    /**
     * 工具类的构造方法
     * @param clazz 对应的对象的CLass对象
     */
    public ExcelImportUtil(Class<T> clazz) {
        this.clazz = clazz;
        this.fields = clazz.getDeclaredFields();
    }

    /**
     * 把excel数据封装到List集合中的方法
     * @param inputStream       excel文件流
     * @param rowIndex          内容开始的行号,行号从0开始
     * @param cellIndex         内容开始的列号,列号也是从0开始
     * @param ext               excel文件的后缀名,有xls和xlsx两种
     * @return
     * @throws Exception
     */
    public List<T> readList(InputStream inputStream, int rowIndex, int cellIndex,String ext) throws Exception {

        List<T> personList = new ArrayList<>();
        T entity = null;
        //根据文件流创建工作簿
        Workbook workbook = null;
        if ("xlsx".equals(ext)) {
            workbook = new XSSFWorkbook(inputStream);
        }else if("xls".equals(ext)){
            workbook = new HSSFWorkbook(inputStream);
        }

        if(workbook == null){
            return null;
        }

        Sheet sheet = workbook.getSheetAt(0);//得到第一个sheet,所以要求我们的内容要放到excel文件的第一个sheet中
        int lastRowNum = sheet.getLastRowNum();//得到有内容的最大的行号
        System.out.println("内容的最大行号:"+lastRowNum);

        for(int rowNum = rowIndex; rowNum<lastRowNum; rowNum++){
            Row row = sheet.getRow(rowNum);//获取行对象
            entity = (T)clazz.newInstance();//通过反射创建实例

            int lastCellNum = row.getLastCellNum();//得到该行内容的最大列号
            for(int cellNum = cellIndex; cellNum<lastCellNum; cellNum++){
                Cell cell = row.getCell(cellNum);//得到该单元格对象
                for(Field field : fields){
                    if(field.isAnnotationPresent(ExcelAnnotation.class)){
                        field.setAccessible(true);
                        ExcelAnnotation excelAnnotation = field.getAnnotation(ExcelAnnotation.class);
                        int sort = excelAnnotation.sort();
                        if(cellNum == sort-1){
                            //调用方法,根据该列的数据类型获取对应的值
                            Object value = this.covertAttrType(field,cell);
                            field.set(entity,value);
                        }
                    }
                }
            }
            personList.add(entity);
        }
        return personList;
    }

    /**
     * 根据单元格的数据类型获取对应的数据值
     * @param field     属性对象
     * @param cell      单元格对象
     * @return
     */
    private Object covertAttrType(Field field, Cell cell) throws ParseException {
        String fieldType = field.getType().getSimpleName();//得到单元格的数据类型名称
        Object value = null;
        if ("String".equals(fieldType)) {
            value = getCellValue(cell);
        }else if ("Date".equals(fieldType)) {
            value =  new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(getCellValue(cell)) ;
        }else if ("int".equals(fieldType) || "Integer".equals(fieldType)) {
            value =  Integer.parseInt(getCellValue(cell));
        }else if ("double".equals(fieldType) || "Double".equals(fieldType)) {
            value =  Double.parseDouble(getCellValue(cell));
        }
        return value;
    }

    /**
     * 根据单元格得到单元格的数据值,把内容格式化为字符串,单元格的数据类型如下:
     *      int CELL_TYPE_NUMERIC = 0;
     *     int CELL_TYPE_STRING = 1;
     *     int CELL_TYPE_FORMULA = 2;
     *     int CELL_TYPE_BLANK = 3;
     *     int CELL_TYPE_BOOLEAN = 4;
     *     int CELL_TYPE_ERROR = 5;
     * @param cell  单元格对象
     * @return
     */
    private String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case 1: //1表示字符串
                return cell.getRichStringCellValue().getString().trim();
            case 0: //0表示数字
                if (DateUtil.isCellDateFormatted(cell)) {
                    Date date = DateUtil.getJavaDate(cell.getNumericCellValue());
                    return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date);
                } else {
                    // 防止数值变成科学计数法
                    String strCell = "";
                    Double num = cell.getNumericCellValue();
                    BigDecimal bd = new BigDecimal(num.toString());
                    if (bd != null) {
                        strCell = bd.toPlainString();
                    }
                    // 去除 浮点型 自动加的 .0
                    if (strCell.endsWith(".0")) {
                        strCell = strCell.substring(0, strCell.indexOf("."));
                    }
                    return strCell;
                }
            case 4://4表示boolean类型
                return String.valueOf(cell.getBooleanCellValue());
            default://其他类型没有处理都是返回空
                return "";
        }
    }
}

 

5、控制器Controller

编写控制器,接收一个excel文件,返回我们从excel读取到的List<对象>的集合。

package com.poi.imports.controller;

import com.poi.imports.pojo.Person;
import com.poi.imports.util.ExcelImportUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;

/**
 * 测试excel导入的控制器
 */
@Controller
@RequestMapping("/import")
public class ImportController {
    @RequestMapping("/persons")
    @ResponseBody
    public List<Person> importPersons(@RequestParam("file") MultipartFile file){
        List<Person> personList = null;
        try {
            InputStream inputStream = file.getInputStream();//得到上传的文件流
            String originalFilename = file.getOriginalFilename();
            String ext = originalFilename.substring(originalFilename.lastIndexOf(".")+1);
            int rowIndex = 2;//内容开始的行
            int cellIndex = 0;//内容开始的列
            personList = new ExcelImportUtil<Person>(Person.class).readList(inputStream,rowIndex,cellIndex,ext);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return personList;
    }
}

 

6、测试

测试方式还是使用postman工具,

 

点击Send,可以看到返回结果:

[
    {
        "pid": "001",
        "name": "王发财",
        "role": "父亲",
        "job": "设计师",
        "hobby": "抽烟喝酒",
        "birthday": "1968-01-01T16:00:00.000+0000",
        "money": 100000.9
    },
    {
        "pid": "002",
        "name": "李华花",
        "role": "母亲",
        "job": "医生",
        "hobby": "唱歌",
        "birthday": "1969-03-02T16:00:00.000+0000",
        "money": 200000.05
    },
    {
        "pid": "003",
        "name": "王有才",
        "role": "哥哥",
        "job": "船长",
        "hobby": "打球",
        "birthday": "1990-08-03T15:00:00.000+0000",
        "money": 10000.8
    },
    {
        "pid": "004",
        "name": "王富贵",
        "role": "弟弟",
        "job": "售货员",
        "hobby": "打牌",
        "birthday": "1993-04-04T16:00:00.000+0000",
        "money": 9000.5
    }
]

拿到了这些数据,我们接下来就可以根据业务做相关的操作了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值