poi使用Excel模板下载数据 : https://blog.csdn.net/kangshifu007/article/details/103174664
步骤 :
1.添加上传需要的依赖
2.在对应的实体类中添加构造方法(与普通构造稍有不同)
3.在controller层编写解析上传Excel文件的代码
4.在service层调用之前编写代码(我这里是调用的之前编写过的save方法 , 因为save中有复杂的逻辑,如果没有重新编写保存的逻辑代码)
注 : ContractProduct 只是一个实体类
上传文件3要素 :
1.form表单提交方式为 method="post"
2.表单属性 : enctype="multipart/form-data"
3.标签类型为file :
org.apache.poi
poi
4.0.1
org.apache.poi
poi-ooxml
4.0.1
org.apache.poi
poi-ooxml-schemas
4.0.1
com.google.code.gson
gson
2.8.5
commons-fileupload
commons-fileupload
1.3.1
说明 : 代码中使用实体类是 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;
}
}
说明 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 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;
}
说明 : 遍历集合 , 使用循环调用添加方法就可实现批量添加
//添加方法
@Override
public void save(ContractProduct contractProduct) {
//单个添加的代码
......具有一定逻辑的添加代码......
}
//通过上传Excel文件批量添加的代码
@Override
public void saveList(List list) {
for (ContractProduct contractProduct : list) {
//不能这么直接添加 , 添加一个数据可能要次改其他表数据 , 需要一定逻辑 , 如果没有逻辑可以这么写
//contractProductDao.insertSelective(contractProduct);
//直接调用之前单个添加方法的代码即可 , 可能添加过程需要逻辑 , 没有必要把逻辑重写一遍
save(contractProduct);
}
}