欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

poi上传Excel文件批量添加数据

程序员文章站 2022-07-13 13:09:04
...

Excel文件上传批量添加数据

步骤 : 

1.添加上传需要的依赖

2.在对应的实体类中添加构造方法(与普通构造稍有不同)

3.在controller层编写解析上传Excel文件的代码

4.在service层调用之前编写代码(我这里是调用的之前编写过的save方法 , 因为save中有复杂的逻辑,如果没有重新编写保存的逻辑代码)

注 : ContractProduct  只是一个实体类

上传文件3要素 : 

1.form表单提交方式为  method="post"

2.表单属性 : enctype="multipart/form-data"

3.标签类型为file : <input name="pic" type="file" ><br>

1.在pom文件添加必要的依赖

        <!--poi的依赖包-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

        <!--文件上传-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

2.在对应的实体类中添加构造方法(与普通构造稍有不同)

说明 : 代码中使用实体类是  ContractProduct  , 实体类中的代码仅供理解 , 只是为了说明实体类中的此构造方法的写法 , 需要根据具体情况修改

public class ContractProduct implements Serializable {

    //实体类属性
    private String id;
    private String productNo;	
    private String productImage;
    private String productDesc;	
    private String loadingRate;	
    private Integer boxNum;		
    private String packingUnit;	
    private Integer cnumber;	
    private Integer outNumber;	
    private Integer finished;	
    private String productRequest
    private Double price;		
    private Double amount;		
    private Integer orderNo;	
    private String contractId;   
    private String factoryName;	
    private String factoryId;


    //构造方法
    //与普通改造不同之处是:Object []objs  不是实体类的属性 , 是传入的一个参数
      public ContractProduct(Object []objs, String companyId, String companyName,String contractId) {

        //objs是一个存放下面属性值的一个数组 , 通过遍历数组方式把Excel中读取的数据逐个赋值给对象属性 , 逐个遍历赋值的前提是Excel中数据顺序要和属性相对应
		this.factoryName = objs[1].toString();
		this.productNo = objs[2].toString();
		this.cnumber = ((Double) objs[3]).intValue();
		this.packingUnit = objs[4].toString();
		this.loadingRate = objs[5].toString();
		this.boxNum = ((Double) objs[6]).intValue();
		this.price = (Double) objs[7];			//单价
		this.productRequest=objs[8].toString();
		this.productDesc=objs[9].toString();
		this.companyId = companyId;
		this.companyName = companyName;
		this.contractId = contractId;
	}

}

3.在controller层编写解析上传Excel文件的代码

说明 1 : 只有两个核心方法  , 第一个方法  inportExcel  根据实际导入的 Excel 文件需要做具体的修改, 代码中第一个for循环索引是1 , 是从文件第二行开始读取的 , 第一行一般是标题 , 如果需要读取第一行那么就修改索引为0

 说明 2 : getCellValue不需要做修改, 这里代码只判断了三种数据类型 , string , 数字(包含日期) , boolean , 一般为三个 , 如果不够可以在switch中添加判断类型

说明 3 (坑):   获取行数: 获取的是行数的最大索引                   获取列数:获取的是最大的列数 ,而不是索引

    @RequestMapping("/import")
    public String importExcel(String contractId, MultipartFile file) throws IOException {

        //1.解析excel获得货物的数据
        List<ContractProduct> list = new ArrayList<>();
        //1.1 工作薄  file =>流
        Workbook workbook = new XSSFWorkbook(file.getInputStream());
        //1.2 得到表
        Sheet sheet = workbook.getSheetAt(0);
        //1.3 得到行
        int lastRowNum = sheet.getLastRowNum();
        Row row = null;
        Cell cell = null;
        for (int i = 1; i <= lastRowNum; i++) {
            //获得行
            row = sheet.getRow(i);
            //1.4 得到单元格
            short lastCellNum = row.getLastCellNum();

            //ContractProduct contractProduct = new ContractProduct();
            Object[] param = new Object[lastCellNum]; //一个数组就是一个对象
            for (int j = 1; j < lastCellNum; j++) {//获得每一个单元格的数据
                //每一个单元格
                cell = row.getCell(j);
                Object cellValue = getCellValue(cell);//获得每一个单元格的值 将每一个值 放入到对象中的对应属性上
                //将数据先存入数组中
                param[j] = cellValue;//  [ null , 值,值,值,值,值,值]
            }
            //封装对象 , 可在第二步中查看具体的封装过程
            ContractProduct contractProduct = new ContractProduct(param, super.companyId, super.companyName, contractId);
            //1.5 获得数据 组装对象
            list.add(contractProduct);
        }
              
        
        //把读取的数据传入service层中 , 需要控制事务
        contractProductService.saveList(list);

        request.setAttribute("contractId", contractId);
        return "redirect:/cargo/contract/list.do";
    }
    
    /*
     * 传入的单元格的数据不同 , 获得不同的数据
     * */
    private static Object getCellValue(Cell cell) {
        Object o = null;
        //获得单元格的类型
        CellType cellType = cell.getCellType();
        switch (cellType) {
            case STRING:
                o = cell.getStringCellValue();//字符串数据
                break;
            case NUMERIC: //数字类型  , 在excel中日期类型是数字类型
                //判断是日期类型 还是数字类型
                if (DateUtil.isCellDateFormatted(cell)) { //是不是日期类型
                    //是日期类型
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                    o = simpleDateFormat.format(cell.getDateCellValue());
                } else {
                    //数字类型
                    o = cell.getNumericCellValue();
                }
                break;
            case BOOLEAN:
                o = cell.getBooleanCellValue();//获得布尔类型数据
                break;
            default:
                break;
        }
        return o;
    }

.4.在service层调用之前编写代码(我这里是调用的之前编写过的save方法 , 因为save中有复杂的逻辑,如果没有重新编写保存的逻辑代码)

说明 : 遍历集合 , 使用循环调用添加方法就可实现批量添加

 //添加方法
    @Override
    public void save(ContractProduct contractProduct) {
        //单个添加的代码
        ......具有一定逻辑的添加代码......

    }



     //通过上传Excel文件批量添加的代码
    @Override
    public void saveList(List<ContractProduct> list) {
        for (ContractProduct contractProduct : list) {    
             //不能这么直接添加 , 添加一个数据可能要次改其他表数据 , 需要一定逻辑 ,  如果没有逻辑可以这么写
            //contractProductDao.insertSelective(contractProduct);  

            //直接调用之前单个添加方法的代码即可 , 可能添加过程需要逻辑 , 没有必要把逻辑重写一遍
            save(contractProduct);
        }
    }