POI读取Excel数据保存到数据库,并反馈给用户处理信息(导入带模板的数据)
今天遇到这么一个需求,将课程信息以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);
POI读取Excel数据保存到数据库,并反馈给用户处理信息(导入带模板的数据)的更多相关文章
- Java web项目 Jxl 读取excel 并保存到数据库,(从eclipse上移动到tomact服务器上,之路径更改,)
最开始在eclipse中测试的时候,并没有上传到服务器上,后来发现,想要读取数据必须上传服务器然后把文件删除就可以了,服务器不可以直接读取外地的文件.用到jxl 1.上传到服务器 前端 <for ...
- Java 用jxl读取excel并保存到数据库(此方法存在局限,仅限本地电脑操作,放在服务器上的项目,需要把文件上传到服务器,详细信息,见我的别的博客)
项目中涉及到读取excel中的数据,保存到数据库中,用jxl做起来比较简单. 基本的思路: 把excel放到固定盘里,然后前段页面选择文件,把文件的名字传到后台,再利用jxl进行数据读取,把读取到的数 ...
- java用poi读取Excel表格中的数据
Java读写Excel的包是Apache POI(项目地址:http://poi.apache.org/),因此需要先获取POI的jar包,本实验使用的是POI 3.9稳定版.Apache POI 代 ...
- 项目一:第四天 1、快递员的条件分页查询-noSession,条件查询 2、快递员删除(逻辑删除) 3、基于Apache POI实现批量导入区域数据 a)Jquery OCUpload上传文件插件使用 b)Apache POI读取excel文件数据
1. 快递员的条件分页查询-noSession,条件查询 2. 快递员删除(逻辑删除) 3. 基于Apache POI实现批量导入区域数据 a) Jquery OCUpload上传文件插件使用 b) ...
- 使用poi读取excel数据示例
使用poi读取excel数据示例 分两种情况: 一种读取指定单元格的值 另一种是读取整行的值 依赖包: <dependency> <groupId>org.apache.poi ...
- JAVA从文本文件(txt)读取一百万条数据保存到数据库
Java读取大文本文件保存到数据库 1.追求效率 将文件读取到内存,效率比较高,经过测试读取1G左右的文本文件,机器内存消耗达到接近3个G,对内存消耗太大,不建议使用 2.通过调用第三方类库实现 通过 ...
- POI读取Excel数据
POI读取Excel表格数据 * {所需相关jar下载: * commons-collections4-4.4.jar * commons-compress-1.19.jar * poi-4.1.1. ...
- Java开发小技巧(六):使用Apache POI读取Excel
前言 在数据仓库中,ETL最基础的步骤就是从数据源抽取所需的数据,这里所说的数据源并非仅仅是指数据库,还包括excel.csv.xml等各种类型的数据接口文件,而这些文件中的数据不一定是结构化存储的, ...
- POI读取Excel内容格式化
在用POI读取Excel内容时,经常会遇到数据格式化的问题. 比如:数字12365会变为12365.0;字符串数字123也会变为123.0,甚至会被变为科学计数法.另外日期格式化也是一个头疼的问题.其 ...
随机推荐
- 父类是在子类创建对象时候 在子类中创建一个super内存空间
父类是在子类创建对象时候 在子类中创建一个super内存空间
- c# 方法参数 params 的试用
//添加方法 public void test(params string[] messages) { } //调用方法 test("aaa","bbb",&q ...
- SpringBoot入门系列HelloWorld
根据咱们程序员学习的惯例,学习一门新技术都是从HelloWorld开始的. 感觉编程是一件非常富有意义的事情,程序员也是一群可爱的人,渴望被关怀和关注,因为我们总在和世界say Hi. 好了进入正题 ...
- java10 新特性 详解
引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...
- HTML的标签元素分类的区别
HTML ,即Hyper Text Markup Language 超文本标记语言: 文本:纯字符,如window中的txt文本 超文本:在纯文本中嵌入样式,图片,音频,视频,链接等内容 HTML的基 ...
- Java的内存结构
Java中的内存结构 运行时数据区域的划分: 程序计数器(PC寄存器) 程序计数器(Program Counter Register)是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器 ...
- Android系统权限和root权限
Android系统权限和root权限http://www.verydemo.com/demo_c189_i277.html
- [CQOI2016] 手机号码 (数位dp)
link $solution:$ $10^{10} \leq L \leq R < 10^{11}$这个数据范围很容易想到数位$dp$. 依照题意模拟即可. #include<iostre ...
- python之旅:python中range()和len()函数区别
函数:len() 作用:返回字符串.列表.字典.元组等长度 语法:len(str) 参数: str:要计算的字符串.列表.字典.元组等 返回值:字符串.列表.字典.元组等元素的长度 实例 1.计算字符 ...
- python【数据类型:集合】