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,甚至会被变为科学计数法.另外日期格式化也是一个头疼的问题.其 ...
随机推荐
- C#实体对象序列化成Json,并让字段的首字母小写
引言:最近在工作中遇到与某些API对接的post的数据需要将对象的字段首字母小写.解决办法有两种:第一种:使用对象的字段属性设置JsonProperty来实现(不推荐,因为需要手动的修改每个字段的属性 ...
- UVA10759_Dice Throwing
求掷骰子n次,点数之和超过m的概率有多大?分数表示. 两种方法: 1.直接DP.用两个数组分别表示分子和分母,注意计算过程中时时约分. 2.将(x1+x2+x3+x4+x5+x6)n多项式展开,把大于 ...
- Alpha 冲刺 —— 十分之三
队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 协助后端界面的开发 搭建项目运行的服务器环境 ...
- 【BZOJ4059】Non-boring sequences
Solution 记序列为\(a\),计算出与\(a_i\)相等的前一个元素的位置\(pre_i\),以及后一个元素的位置\(nex_i\),显然,对于那些左端点处于\((pre_i,i]\)以及右端 ...
- 洛谷 P1306 斐波那契公约数 解题报告
P1306 斐波那契公约数 题意:求\(Fibonacci\)数列第\(n\)项和第\(m\)项的最大公约数的最后8位. 数据范围:\(1<=n,m<=10^9\) 一些很有趣的性质 引理 ...
- Metasploit+python生成免杀exe过360杀毒
Metasploit+python生成免杀exe过360杀毒 1在kali下生成一个反弹的msf的python脚本,命令如下: msfvenom -p windows/meterpreter/reve ...
- 设计模式 (一)——策略模式(Strategy,行为型)
1.概述 使用设计模式可以提高代码的可复用性.可扩充性和可维护性.策略模式(Strategy Pattern)属于行为型模式,其做法是将类所需的行为或者算法一个个封装成单独的类,并将其作为类的数据成员 ...
- String,static,final
1. String 下面代码创建了几个对象? String s1 = new String("Hello"); String s2 = new String("Hello ...
- bzoj 2428: [HAOI2006]均分数据 && bzoj 3680 : 吊打XXX 模拟退火
每次把元素随便扔随机一个初始解,退火时每次随机拿一个元素扔到随机一个集合里,当温度高时因为状态不稳定扔到那个元素和最小的里边. 如果新解优,更新ans. 把原式拆一下,就可以用int存了. bzoj ...
- 【裴蜀定理】【CF1091C】 New Year and the Sphere Transmission
Description 有 \(n\) 个人围成一个圈,按照顺时针从 \(1\) 到 \(n\) 编号.第 \(1\) 个人会拿到一个球,他指定一个数字 \(k\),然后会将球传给他后面顺指针数第 \ ...