今天遇到這么一個需求,將課程信息以Excel的形式導入數據庫,並且課程編號再數據庫中不能重復,也就是我們需要先讀取Excel提取信息之后保存到數據庫,並將處理的信息反饋給用戶。於是想到了POI讀取文件提取數據,也可以利用Jxl讀取Excel提取數據。
最終效果:
對於下面的Excel,總共20條數據。18條在數據庫已經存在,最后兩條是在同一個excel文件中重復在數據庫不存在。

反饋結果:(也就是最后兩個X6511只保存了一條)

思路:
1.先將Excel文件上傳到本地,保存到本地磁盤
2.讀取本地磁盤的Excel,並且提取數據封裝成集合。
3.對提取的信息進行處理,也就是保存數據庫,保存數據庫之前先判斷是否已經存在相同的編號,如果存在就不保存數據庫,並且將存在的編號記錄到一個集合中,最后根據此集合返回給用戶信息。
前端文件上傳是layui,后端接收文件是springMVC,處理Excel是POI
0.界面准備文件上傳的button
<button class="layui-btn layui-btn-warm" type="button" id="importCoursesBtn" style="float: right"><i class="layui-icon"></i>導入課程</button>
1.前端:layui的文件上傳JS
/********S 導入課程相關操作******/ layui.use(['layer','upload'],function () {//使用文件上傳和layer模塊 var layer =layui.layer,upload = layui.upload; var uploadInst = upload.render({ elem: '#importCoursesBtn',//綁定的元素 url: contextPath+'/uploadCourseExcel.do',//提交的url auto:true,//是否自動上傳 accept:"file",//指定允許上傳的文件類型 multiple:false,//支持多文件上傳 exts:'xls|xlsx', done: function(res, index, upload){ //假設code=0代表上傳成功 layer.close(layer.index); //它獲取的始終是最新彈出的某個層,值是由layer內部動態遞增計算的 layer.alert(res.msg); } }); }) /********E 導入課程相關操作******/
2.后端Controller層代碼:
主要就是:
保存文件到本地
讀取本地的excel文件,提取數據
處理提取之后的數據(也就是調用service層對提取的數據集合進行保存)
根據Service返回的重復的編號的集合以及提取的數據集合判斷添加結果並反饋給用戶。
package cn.xm.jwxt.controller.trainScheme; import cn.xm.jwxt.bean.trainScheme.TCourseBaseInfo; import cn.xm.jwxt.service.trainScheme.CourseBaseInfoService; import cn.xm.jwxt.utils.FileHandleUtil; import cn.xm.jwxt.utils.ResourcesUtil; import cn.xm.jwxt.utils.ResposeResult; import cn.xm.jwxt.utils.UUIDUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.log4j.Logger; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * @Author: qlq * @Description 導入課程信息(以Excel模板的形式導入) * @Date: 11:04 2018/5/5 */ /** * 導入課程(以Excel的形式導入) * 1.導入文件,將文件保存到本地 * 2.讀取Excel提取課程信息 * 3.進行數據庫保存 * 4.反饋導入信息 */ @Controller public class ImportCourseExcel { private Logger logger = Logger.getLogger(ImportCourseExcel.class);//日志記錄器 @Autowired private CourseBaseInfoService courseBaseInfoService;//課程service /** * 導入課程信息(以課程信息導入) * @param file * @return */ @RequestMapping("/uploadCourseExcel") public @ResponseBody ResposeResult uploadCourseExcel(MultipartFile file){ ResposeResult resposeResult = new ResposeResult(); String fileOriName = null; String fileNowName = null; if(file == null){ resposeResult.setMsg("請上傳正確的Excel文件"); return resposeResult; } //1.保存文件到本地 fileOriName = file.getOriginalFilename();//獲取原名稱 fileNowName = UUIDUtil.getUUID2()+"."+ FilenameUtils.getExtension(fileOriName);//生成唯一的名字 try { fileNowName = FileHandleUtil.uploadSpringMVCFile(file, "courseExcelFileImport", fileNowName);//保存文件 } catch (Exception e) { resposeResult.setMsg("請上傳正確的Excel文件"); logger.error("導入課程信息失敗失敗",e); } //2.讀取文件 String fileQualifyName = ResourcesUtil.getValue("path","courseExcelFileImport")+fileNowName;//生成文件全路徑 List<TCourseBaseInfo> tCourseBaseInfos = this.readExcelData(fileQualifyName);//讀取的Excel數據 if(tCourseBaseInfos == null || tCourseBaseInfos.size()==0){ resposeResult.setMsg("您上傳的文件沒有課程信息,請重新編輯"); return resposeResult; } //3.保存數據庫 List<String> repeatCourseNums = null; try { repeatCourseNums = courseBaseInfoService.addCourseBaseInfoBatch(tCourseBaseInfos); } catch (SQLException e) { resposeResult.setMsg("保存數據庫的時候出錯"); logger.error("保存數據庫出錯"); } //4.根據返回結果判斷重復的數據與條數。 int allTotal = tCourseBaseInfos.size(); // 4.1如果重復的集合為空則證明全部上傳成功 if(repeatCourseNums == null || repeatCourseNums.size()==0){ resposeResult.setMsg(allTotal+"條課程信息全部上傳成功"); }else {//4.2如果有重復提示哪些重復了 int repeatSize = repeatCourseNums.size(); resposeResult.setMsg("總共"+allTotal+"條數據,成功上傳"+(allTotal - repeatSize)+"條,重復了"+repeatSize+"條。"+"重復的課程編號為"+repeatCourseNums.toString()); } return resposeResult; } /** * 讀取Excel提取數據(返回提取的數據集合) * @param fileQualifyName * @return */ private List<TCourseBaseInfo> readExcelData(String fileQualifyName) { List<TCourseBaseInfo> datas = null; File file = new File(fileQualifyName); try { // 獲取一個工作簿 HSSFWorkbook workbook = new HSSFWorkbook(FileUtils.openInputStream(file)); // 獲取一個工作表兩種方式 // HSSFSheet sheet = workbook.getSheet("sheet0"); // 獲取工作表的第二種方式 HSSFSheet sheet = workbook.getSheetAt(0); int firstRow = 1; // 獲取sheet的最后一行 int lastRow = sheet.getLastRowNum(); if(lastRow <2){//如果只有1行或者0行就直接退出 return null; } datas = new ArrayList<TCourseBaseInfo>();//用於返回的數據集合 //循環內不要創建對象引用(集合中存的是對象的引用) TCourseBaseInfo courseBaseInfo = null; for(int i=firstRow;i<=lastRow;i++){ courseBaseInfo = new TCourseBaseInfo(); HSSFRow row = sheet.getRow(i); int lastCol = row.getLastCellNum(); if(lastCol != 14){ //如果不是14列就不讀這一行了。 continue; } for(int j=0;j<lastCol;j++){ HSSFCell cell= row.getCell(j);//獲取一個cell if (j == 0) { courseBaseInfo.setCoursenum(cell.getStringCellValue());//課程編號 continue; } if (j == 1) { courseBaseInfo.setCourseplatform(cell.getStringCellValue());//課程平台 continue; } if (j == 2) { courseBaseInfo.setCoursenature(cell.getStringCellValue());//課程性質 continue; } if (j == 3) { courseBaseInfo.setCoursenamecn(cell.getStringCellValue());//中文名稱 continue; } if (j == 4) { courseBaseInfo.setCoursenameen(cell.getStringCellValue());//英文名稱 continue; } if (j == 5) { courseBaseInfo.setCredit(cell.getStringCellValue());//學分 continue; } if (j == 6) { courseBaseInfo.setCoursehour(cell.getStringCellValue());//學時 continue; } if (j == 7) { courseBaseInfo.setTeachhour(cell.getStringCellValue());//講課時長 continue; } if (j == 8) { courseBaseInfo.setExperimenthour(cell.getStringCellValue());//實驗時長 continue; } if (j == 9) { courseBaseInfo.setComputerhour(cell.getStringCellValue());//上機時長 continue; } if (j == 10) { courseBaseInfo.setPracticehour(cell.getStringCellValue());//實踐時長 continue; } if (j == 11) { courseBaseInfo.setWeeklyhour(cell.getStringCellValue());//周學時分配 continue; } if (j == 12) { courseBaseInfo.setScoringway(cell.getStringCellValue());//計分方式 continue; } if (j == 13) { courseBaseInfo.setCoursehourmethod(cell.getStringCellValue());//學時單位 continue; } } //讀完一行將數據塞進去 datas.add(courseBaseInfo); } } catch (IOException e) { logger.error("讀取上傳的Excel出錯"); } return datas; } }
Service對提取到的list集合進行批量保存的代碼:
主要就是遍歷集合,獲取課程編號判斷數據庫中是否已經存在相同編號的數據,如果已經存在則此條數據不保存數據庫並將編號加到重復的list集合。
@Override public int getCountByCourseNum(String courseNum) throws SQLException { return tCourseBaseInfoCustomMapper.getCountByCourseNum(courseNum); } @CacheEvict(value = "coursesFy",allEntries =true )//清掉分頁的redis緩存 @Override public boolean addCourseBaseInfo(TCourseBaseInfo courseBaseInfo) throws SQLException { //如果傳下來的課程信息的id為空,就用UUID生成一個ID if(ValidateCheck.isNull(courseBaseInfo.getCourseid())){ courseBaseInfo.setCourseid(UUIDUtil.getUUID2()); } // remark1用於標記是否正在使用,1代表正在使用,0代表已經刪除。 if(ValidateCheck.isNull(courseBaseInfo.getRemark1())){ courseBaseInfo.setRemark1(DefaultValue.IS_USE); } return tCourseBaseInfoMapper.insert(courseBaseInfo)>0?true:false; } @Override public List<String> addCourseBaseInfoBatch(List<TCourseBaseInfo> courseBaseInfos) throws SQLException { //1.遍歷集合進行添加。 //1.1如果已經存在相同的課程編號,將該課程的編號加到返回的集合中,用於提示哪些編號重復了 List<String> repeatCourseNums = new ArrayList<String>(); for(TCourseBaseInfo tCourseBaseInfo :courseBaseInfos){ //如果課程編號為空結束本次循環開始下一次 if(ValidateCheck.isNull(tCourseBaseInfo.getCoursenum())){ continue; } //根據數據庫是否已經存在相同的課程編號決定是否可以保存課程信息 int result = this.getCountByCourseNum(tCourseBaseInfo.getCoursenum()); if(result >= 1){//如果存在就不添加並保存到重復的元素集合 repeatCourseNums.add(tCourseBaseInfo.getCoursenum()); }else {//不存在就可以添加 this.addCourseBaseInfo(tCourseBaseInfo); } } return repeatCourseNums; }
解釋:
1.文件保存的工具方法:
/******* S針對SptingMVC的上傳文件的處理 *************/ /** * 專門針對SpringMVC的文件上傳操作 * @param multipartFile 文件參數 * @param propertiesKey 需要讀取的path里面的key * @param fileName 文件名字,比如: ce5bd946fd43410c8a26a6fa1e9bf23c.pdf * @return 返回值是最后的文件名字,如果是word需要轉成pdf,1.doc返回值就是1.pdf */ public static String uploadSpringMVCFile(MultipartFile multipartFile,String propertiesKey,String fileName) throws Exception { String fileDir = FileHandleUtil.getValue("path", propertiesKey);// 獲取文件的基本目錄 //1.將文件保存到指定路徑 multipartFile.transferTo(new File(fileDir+fileName));//保存文件 //2.根據文件后綴判斷文件是word還是pdf,如果是word需要轉成pdf,其他的話不做處理 String fileNameSuffix = FilenameUtils.getExtension(fileName);//調用io包的工具類獲取后綴 if("doc".equals(fileNameSuffix)||"docx".equals(fileNameSuffix)){//如果后綴是doc或者docx的話轉為pdf另存一份 String fileNamePrefix = FilenameUtils.getBaseName(fileName);//獲取文件前綴名字 Word2PdfUtil.word2pdf(fileDir+fileName,fileDir+fileNamePrefix+".pdf");//進行word轉換pdf操作 fileName = fileNamePrefix+".pdf";//並將文件的名字換成新的pdf名字 } return fileName; } /******* E針對SptingMVC的上傳文件的處理 *************/
補充:今天在讀取數字111的時候遇到這樣一個問題,讀取111為字符串報錯,解決辦法:
cell.setCellType(Cell.CELL_TYPE_STRING);
