前言:
业务场景:用户提供Excel表,在页面上点击“导入按钮”,系统读取Excel中的数据,存到对应的数据库
注:
1,目前仅提供导入简单的Excel表,没有合并单元格,只能读取单页sheet
2,方法里用到了具体业务场景的实体类,所以适用性不是很强,其他场景要用的话,还需要修改代码。但是也具有一定的参考性,所以先记录下来。我的后续思路是:在 工具类(ReadExcelUtil) 里根据导入的Excel表头确定要处理的字段名,然后拼成json格式的数据,传到Service层,再做处理。之后再做更新 //TODO
3,用的jar包:POI
正文:
html:
<div> <span><input type="file" id="upfile" name="upfile" placeholder=""/></span> <button onclick="importExp();">导入</button> <span>格式:.xls</span> </div>
JS:
function importExp() { var name = $("#upfile").val(); var file = $("#upfile")[0].files[0]; // ajax... }
Java:
Controller层
@ApiOperation(value = "导入Excel") @RequestMapping(value="excel/import", method = RequestMethod.POST) public void importExcel(MultipartFile file) { return this.theService.importExcel(file); }
Service层
public void importExcel(MultipartFile file) { if(file.isEmpty()){ //请先选择Excel文件。是否把报错等信息返回到前端,视情况而定。 return; } Result readResult = ReadExcelUtil.readExcel(file); //Result是封装了返回值的类,相当于实体类 if(readResult.getCode() != 0){ //错误:readResult.msg return; } List<Award> list = (List<Award>) readResult.getData(); //Award是数据库表对应的实体类 //获取了list之后,insert到数据库... }
工具类:
ReadExcelUtil
package com.bf.base.utils; import com.bf.base.entity.DripAward; import com.bf.base.params.Result; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.multipart.MultipartFile; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; public class ReadExcelUtil { /** * 读取 单页sheet,返回一个集合 * @return */ public static Result<?> readDripAwardExcel(MultipartFile file) { Result result = new Result<>(); InputStream is = null; Workbook wb = null; String fileName = file.getOriginalFilename(); String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()); try { is = file.getInputStream(); if (fileType.equals("xls")) { wb = new HSSFWorkbook(is); } else if (fileType.equals("xlsx")) { wb = new XSSFWorkbook(is); } else { return new Result<>(Result.FAIL, "读取的不是excel文件", null, null); } int sheetSize = wb.getNumberOfSheets();//有多少sheet页 if(sheetSize >= 2){ return new Result<>(Result.FAIL, "请核对Excel的页数", null, null); } Sheet sheet = wb.getSheetAt(0); result = sheetData2List(sheet); //关键,sheet表数据 转 集合 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return result; } //获取数据 private static Result sheetData2List(Sheet sheet) { Result result = new Result<>(); List<Award> awards = new ArrayList<>(); int CELL_SIZE = 3; //excel固定三列(编码、金额) int rowSize = sheet.getLastRowNum() + 1; for (int j = 0; j < rowSize; j++) { //读取每一行 Row row = sheet.getRow(j); if (row == null) { continue; } if(row.getLastCellNum() != CELL_SIZE){ result.setCode(2); result.setMsg("第"+ j +"行数据异常,请核对后再上传!"); return result; } if (j == 0) { continue; } else { DripAward rowObj = new DripAward(); for (int k = 0; k < CELL_SIZE; k++) { //获取列的数据 Cell cell = row.getCell(k); String value = getCellFormatValue(cell); switch (k) { case 0: rowObj.setCode(AESUtil.encrypt(value)); break; case 1: rowObj.setAmount(Double.parseDouble(value)); break; case 2: rowObj.setClassify(Integer.parseInt(value)); break; default: break; } } awards.add(rowObj); } } result.setCode(0); result.setData(awards); return result; } //获取列的数据 private static String getCellFormatValue(Cell cell) { String cellvalue = ""; if (cell != null) { CellType cellType = cell.getCellTypeEnum(); switch (cellType) { case NUMERIC: { if(String.valueOf(cell.getNumericCellValue()).indexOf("E") == -1){ cellvalue = String.valueOf(cell.getNumericCellValue()); }else { cellvalue = new DecimalFormat("#").format(cell.getNumericCellValue()); } break; } case STRING: cellvalue = cell.getRichStringCellValue().getString(); break; default: cellvalue = "-"; } } return cellvalue; } }
其他:
Result (封装了返回值的类,相当于实体类)
package com.bf.base.params; import org.apache.commons.lang.StringUtils; /** * 传给前端数据的统一格式 * code = 0时,表示调用成功 * 其余code均表示调用接口异常,异常时,标明异常码,并给出msg和detail注释,同步文档 */ public class Result<T> { private int code;//状态返回码 private String msg;//返回码描述 private String detail;//错误详细描述或返回码对应处理方案 private T data;//返回的主体数据 public static final int FAIL = -1; public static final int SUCCESS = 0; @Override public String toString() { return "Result [code=" + code + ", msg=" + msg + ", detail=" + detail + ", data=" + data + "]"; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
Award(数据库表对应的实体类)
public class Award { private String code; private Double amount; private int classify; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Double getAmount() { return amount; } public void setAmount(Double amount) { this.amount = amount; } public int getClassify() { return classify; } public void setClassify(int classify) { this.classify = classify; } }