今天遇到这么一个需求,将课程信息以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);