最近在研究如何通过vue的elemenetui框架加上springboot去实现从前端上传文件给后端,在进行文件查找的时候,大部分我进行查找的都是怎么上传文件给服务器,所以也为了记录,就计划写下本文章;
同时,本次实现上传excel并对excel的内容进行读取
在我的项目中,我需要做一个签到打卡的功能,其中人员方面是希望用导入的方式进行导入,
用的是elementui的upload
组件,在做这个的时候,最麻烦和难搞的是怎么连到后端,虽然说在elementui的官方文档里,写的是对里面的action进行操作,但是看了很多都不知道怎么去进行下一步的操作;好在,经过学习和了解,终于把它给完成了,那么我们来具体看看怎么做吧。
在我的前端项目中,我是用一个按钮封装upload,具体如下
<template>
<div>
<el-upload
style="display: inline-block"
action="string"
:limit="1"
:file-list="fileList"
:on-error="loadFileError"
:on-success="loadFileSuccess"
:before-upload="beforeUpload"
accept=".xlsx,.xls"
:show-file-list="false"
:http-request="uploadFile"
><el-button type="success">导入el-button>el-upload
>
div>
template>
其中
limit: 代表一次可上传的文件数量
file-list: 代表自己定义的属性
on-error: 代表导入文件失败的时候提示的方法
on-success:代表导入文件成功提示的方法
before-upload:代表在上传前检查文件的格式、数据大小、信息等,判定文件是否能够上传
show-file-list:代表是否显示文件列表,false不显示
http-request:本次用来进行上传给后端的方法
然后就是正式的script的代码了。
定义的属性
export default {
name: "conferenceSignCard",
data() {
return {
fileList: [], // 导入的文件
};
},
}
导入文件失败的时候提示的方法
// 导入失败,其中$message为elementui的消息提醒组件
loadFileError() {
this.$message({
message: "文件上传失败!",
type: "error",
});
},
导入文件成功提示的方法
loadFileSuccess() {
this.$message({
message: "文件上传成功!",
type: "success",
});
},
上传前检查文件的格式、数据大小、信息等,判定文件是否能够上传
// 导入前检查文件
beforeUpload(file) {
const extension = file.name.split(".")[1] === "xls";
const extension2 = file.name.split(".")[1] === "xlsx";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!extension && !extension2) {
this.$message({
message: "上传模板只能是 xls、xlsx格式!",
type: "error",
});
}
if (!isLt2M) {
console.log("上传模板大小不能超过 2MB!");
this.$message({
message: "上传模板大小不能超过 2MB!",
type: "error",
});
}
return extension || extension2 || isLt2M;
},
进行上传给后端的方法
// 导入文件
uploadFile(param) {
//this.$router.go(0);
const File = param.file;
let formDataInfo = new FormData();
formDataInfo.append("file", File);
const config = {
url: "/signCard/loadConferenceFile",
method: "post",
data: formDataInfo,
};
// http为我所在项目封装了router的js代码,如果需要传递给后端,需要
// 如上方代码我写的信息进行传递即可
http(config)
.then((res) => {
this.$message({
message: res.data,
type: "success",
});
});
},
或者写成
uploadFile(param){
const File = param.file;
let formDataInfo = new FormData();
formDataInfo.append("file", File);
loadConferenceFile(formDataInfo).then((res) => {
this.$message({
message: res.data,
type: "success",
});
});
},
loadConferenceFile(formDataInfo){
return axios.post("/signCard/loadConferenceFile",formDataInfo)
}
两者二选一,就只是axios的调用形式有区别
那么到这里我们的前端就写完了,它是一个按钮,可导入xlsx和xls文件格式
同时,用这个方式调用其实有一个问题,如果不刷新,就会在每次刷新只能调用一次,所以我有写一个定时器,每次调用后隔一秒进行刷新页面;
当然如果有更好的方式,能够知道为什么会出现这个情况,可以告诉我怎么修改
那么,具体的加定时器后的代码如下
// 导入文件
uploadFile(param) {
//this.$router.go(0);
const File = param.file;
let formDataInfo = new FormData();
formDataInfo.append("file", File);
const config = {
url: "/signCard/loadConferenceFile",
method: "post",
data: formDataInfo,
};
http(config)
.then((res) => {
this.$message({
message: res.data,
type: "success",
});
})
.then(() => {
this.startTimer();
});
},
startTimer() {
this.redurceTime = 1;
this.timer = setInterval(() => {
this.redurceTime--;
if (this.redurceTime == 0) {
this.clearTimer();
this.$router.go(0);
}
}, 1000);
},
clearTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
在这里我添加了这么几个属性
data() {
return {
fileList: [], // 导入的文件
ConferenceList: [], // 会议列表
timer: null, // 定时器
redurceTime: 0, // 剩余时间
};
},
ok,那么现在我们的前端部分正式ok了,由前面我们知道我后端调用的路径为
/signCard/loadConferenceFile
并且为post
调用
那么到后端,我springboot的controller层的代码为:
import com.ht.exam.service.SignCardService;
import org.apache.ibatis.annotations.Param;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.File;
@RestController
@RequestMapping("/signCard/*")
public class SignCardController {
@Resource
private SignCardService signCardService;
// 上传导入会议人员的文件
@PostMapping("/loadConferenceFile")
public String loadConferencePersonFile(@RequestParam("file") MultipartFile file) {
return signCardService.loadConferencePersonFile(file);
}
}
service层代码为
@Service
public class SignCardService {
public String loadConferencePersonFile(MultipartFile file) {
// 在这里写具体的功能逻辑
}
以上,我们就完成了后端的接收了,接收之后,那就需要解析文件
首先,我们需要引入poi的依赖
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>3.9version>
dependency>
然后是具体的读取文件
@Service
public class SignCardService {
public String loadConferencePersonFile(MultipartFile file) {
// 读取文件
InputStream inputStream = null;
try{
inputStream = file.getInputStream();
}catch (IOException e){
return "文件读取失败";
}
// 创建excel实例
Workbook wb = null;
// 判断格式是.xlsx还是.xls
if (file.getOriginalFilename().matches("^.+\\.(?i)(xlsx)$")) {
try {
wb = new XSSFWorkbook(inputStream);
}catch (IOException e) {
return "读取excel(xlsx)失败!";
}
} else {
try {
wb = new HSSFWorkbook(inputStream);
}catch (IOException e) {
return "读取excel(xls)失败!";
}
}
// 获取excel第一个表格
Sheet sheet = wb.getSheetAt(0);
if (sheet == null) {
return new ResultVO(ResultCode.ERROR.getCode(),"该文件没有数据!");
}
// 获取列数
int column = sheet.getRow(0).getPhysicalNumberOfCells();
// 获取行数
int rows = sheet.getLastRowNum();
List<String> empNoList = new ArrayList<>();
// 遍历Excel
for (int i = 1; i <= sheet.getLastRowNum();i++) {
Row row = sheet.getRow(i);
if (row == null) {
continue;
}
// 遍历列数,工号
if (row.getCell(0) !=null) {
row.getCell(0).setCellType(CellType.STRING);
String empNo = row.getCell(0).getStringCellValue();
// 不为空获取工号
if (StringUtils.isNotEmpty(empNo)) {
empNoList.add(empNo);
}
}
}
// 然后以上是读取,往下写自己需要的功能
}
在poi中
sheet指的是
row指的是行
cell指的是列
以上为我实现上传excel文件给后端并读取excel的具体过程