1. 创建一张表,用于接收分片后的文件
2. 根据表创建一个实体类
import java.io.Serializable;
public class SysFileUpload implements Serializable {
private Integer fileId;
private String fileName;
private String filePath;
private String fileSuffix;
private String fileSize;
private Integer fileShardIndex;
private String fileShardSize;
private Integer fileShardTotal;
private String fileKey;
private static final long serialVersionUID = 1L;
public Integer getFileId() {
return fileId;
}
public void setFileId(Integer fileId) {
this.fileId = fileId;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName == null ? null : fileName.trim();
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath == null ? null : filePath.trim();
}
public String getFileSuffix() {
return fileSuffix;
}
public void setFileSuffix(String fileSuffix) {
this.fileSuffix = fileSuffix == null ? null : fileSuffix.trim();
}
public String getFileSize() {
return fileSize;
}
public void setFileSize(String fileSize) {
this.fileSize = fileSize == null ? null : fileSize.trim();
}
public Integer getFileShardIndex() {
return fileShardIndex;
}
public void setFileShardIndex(Integer fileShardIndex) {
this.fileShardIndex = fileShardIndex;
}
public String getFileShardSize() {
return fileShardSize;
}
public void setFileShardSize(String fileShardSize) {
this.fileShardSize = fileShardSize == null ? null : fileShardSize.trim();
}
public Integer getFileShardTotal() {
return fileShardTotal;
}
public void setFileShardTotal(Integer fileShardTotal) {
this.fileShardTotal = fileShardTotal;
}
public String getFileKey() {
return fileKey;
}
public void setFileKey(String fileKey) {
this.fileKey = fileKey == null ? null : fileKey.trim();
}
/**
* 扩展属性
*/
private String base64;
public String getBase64() {
return base64;
}
public void setBase64(String base64) {
this.base64 = base64 == null ? null : base64.trim();
}
/**
* 扩展属性
*/
private String fileFlag;
public String getFileFlag() {
return fileFlag;
}
public void setFileFlag(String fileFlag) {
this.fileFlag = fileFlag == null ? null : fileFlag.trim();
}
}
3. 编写接收分片后的接口
@RequestMapping(value = "/procFile",method = RequestMethod.POST)
@ResponseBody
public Map<String,Object> upload(SysFileUpload fileUpLoad) throws Exception {
Map<String,Object> map = new HashMap<String, Object>() ;
String os = System.getProperty("os.name");
String uploadPath = "";
if (os.toLowerCase().startsWith("win")) {
uploadPath = winUploadPath.replace("/", File.separator);
} else {
uploadPath = linUploadPath;
}
String fileFlag= fileUpLoad.getFileFlag();
String fileDirPath =uploadPath + File.separator + fileFlag;
File targetFile = new File(fileDirPath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
//base64转文件
MultipartFile multipartFile = Base64ToFileUtil.Base64ToFilepart(fileUpLoad.getBase64());
//实际存储路径
String path = fileDirPath+ File.separator +fileUpLoad.getFileKey()+"."+fileUpLoad.getFileShardIndex();
multipartFile.transferTo(new File(path));
//文件的相对路径
fileUpLoad.setFilePath(fileFlag+ File.separator +fileUpLoad.getFileKey());
//保存文件上传的进度信息
sysFileUploadService.saveSysFileUpload(fileUpLoad);
//判断当前分片是否为最后一个
if(fileUpLoad.getFileShardIndex().equals(fileUpLoad.getFileShardTotal())){
map.put("code",100);
map.put("id",fileUpLoad.getFileId());
map.put("src",fileUpLoad.getFilePath()+"."+fileUpLoad.getFileSuffix());
fileUpLoad.setFilePath(fileDirPath+File.separator+fileUpLoad.getFileKey());
//合并分片
merge(fileUpLoad);
return map;
}else{
map.put("code",200);
map.put("shardIndex",fileUpLoad.getFileShardIndex());
}
return map;
}
/**
* 检查文件上传的进度
* @param shardKey
* @return
*/
@RequestMapping("/checkProgress")
@ResponseBody
public Map<String,Object> checkFile(@RequestParam String shardKey){
Map<String,Object> map = new HashMap<String, Object>();
SysFileUpload fileUpLoad = sysFileUploadService.selectByKey(shardKey);
if(fileUpLoad==null){
//新的文件
map.put("code",200);
}else if(fileUpLoad!=null){
map.put("id",fileUpLoad.getFileId());
if(fileUpLoad.getFileShardIndex() < fileUpLoad.getFileShardTotal()){
map.put("code",220);
map.put("ShardIndex",fileUpLoad.getFileShardIndex()+1);
}else if(fileUpLoad.getFileShardIndex().equals(fileUpLoad.getFileShardTotal())){
map.put("code",240);
map.put("src",fileUpLoad.getFilePath()+"."+fileUpLoad.getFileSuffix());
}
}
return map;
}
public void merge(SysFileUpload fileUpLoad) throws Exception {
Map<String,Object> map = new HashMap<String, Object>();
//输出文件
File newfile = new File(fileUpLoad.getFilePath()+"."+fileUpLoad.getFileSuffix());
//文件追加
FileOutputStream outputStream =new FileOutputStream(newfile,true);
//分片文件
FileInputStream fileInputStream=null;
byte[] byt=new byte[10*1024*1024];
int len;
try {
for (int i =0;i< fileUpLoad.getFileShardTotal();i++){
fileInputStream=new FileInputStream(new File(fileUpLoad.getFilePath()+"."+(i+1)));
while ((len=fileInputStream.read(byt))!=-1){
outputStream.write(byt,0,len);
}
}
}catch(IOException e){
System.out.println("合并异常"+e.toString());
}finally {
try{
if(fileInputStream!=null){
fileInputStream.close();
}
outputStream.close();
System.out.println("IO关闭成功");
}catch (Exception e){
System.out.println("IO关闭异常"+e);
}
}
System.gc();
Thread.sleep(100);
//删除分片
for (int i=0;i<fileUpLoad.getFileShardTotal();i++){
String path=fileUpLoad.getFilePath()+"."+(i+1);
File file = new File(path);
boolean delete = file.delete();
}
}
/**
* 完成数据包上传
*
* @param request
* @param fileId
* @return
*/
@RequestMapping(value = "/finishUpload", method = RequestMethod.GET)
@ResponseBody
public Result finishUploadPackage(HttpServletRequest request, @RequestParam("fileId") Integer fileId) {
Map<String, Object> map = new HashMap<>();
// 如果有必要请重命名
map.put("filePath", 文件路径);
map.put("fileName", 文件名称);
map.put("code", 100);
return Result.success(map);
}
4. 前端上传文件
4.1 计算md5值
import SparkMD5 from 'spark-md5'
/** 计算文件md5值
* @param file 文件
* @param chunkSize 分片大小
* @returns Promise
*/
function getmd5(file, chunkSize) {
return new Promise((resolve, reject) => {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
let chunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
let spark = new SparkMD5.ArrayBuffer();
let fileReader = new FileReader();
fileReader.onload = function(e) {
spark.append(e.target.result);
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
let md5 = spark.end();
resolve(md5);
// console.log(md5);
}
};
fileReader.onerror = function(e) {
reject(e);
};
function loadNext() {
let start = currentChunk * chunkSize;
let end = start + chunkSize;
if (end > file.size){
end = file.size;
}
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
});
}
4.2 上传
// 文件上传之前
onBeforeUpload(file){
let text=""
// 获取文件后缀
var fileExt =file.name.substring(file.name.lastIndexOf('.')+1).toLowerCase();
// 文件后缀是否是 zip
const zipExt = fileExt === 'zip'
const exeExt = fileExt === 'exe'
// 文件大小不能超过1G
const isLimit = file.size / 1024 / 1024 < 1024
if(!zipExt && !exeExt) {
text="上传文件只能是 zip或exe 格式!";
this.$message.error(text)
return false;
}
if (!isLimit) {
text="上传文件大小不能超过 1GB!";
this.$message.error(text)
return false;
}
this.fileShardSize = 1*1024 * 1024; //每片文件大小
//点击后隐藏上传按钮 ,防止重复点击
//$("#fileUpload").css('visibility','hidden');
this.showUploadBtn=false
this.showUploadProcess=true
this.percentage=1
var _this=this
getmd5(file,_this.fileShardSize).then(e =>{
_this.switchC=false;
_this.fileShardIndex=1;//分片索引
_this.curFile=file;
_this.fileKey=e;
_this.fileSize=file.size;
_this.fileShardTotal=Math.ceil(file.size/_this.fileShardSize);//分片总数
var fileFullName=file.name;
_this.fileSuffix = fileFullName.substr(fileFullName.lastIndexOf('.') + 1);
_this.fileName = fileFullName.substr(0, fileFullName.lastIndexOf('.'));
//上传参数
var params = new FormData()
params.append('fileName', _this.fileName)
params.append('fileShardTotal', _this.fileShardTotal)
params.append('fileKey', _this.fileKey)
params.append('fileSuffix', _this.fileSuffix)
params.append('fileShardSize', _this.fileShardSize)
params.append('fileSize', _this.fileSize)
params.append('fileFlag', _this.flagType)
_this.updateProgress(file,params)
})
},
// 批量上传
uploadFile (formData) {
var _this=this
// 上传 上边的 procFile 接口
procFile(formData).then(res => {
if(res.data.code==200){
//上传分片完成
if(res.data.shardIndex<_this.fileShardTotal){
_this.fileShardIndex=_this.fileShardIndex+1;
_this.start=(_this.fileShardIndex-1)*_this.fileShardSize;
_this.end =Math.min(_this.curFile.size,_this.start+_this.fileShardSize);
_this.fileSize=_this.curFile.size;
var params = new FormData()
params.append('fileName', _this.fileName)
params.append('fileShardTotal', _this.fileShardTotal)
params.append('fileKey', _this.fileKey)
params.append('fileSuffix', _this.fileSuffix)
params.append('fileShardSize', _this.fileShardSize)
params.append('fileSize', _this.fileSize)
params.append('fileFlag', _this.flagType)
params.append('fileShardIndex', _this.fileShardIndex)
var fileShardtem=_this.curFile.slice(_this.start,_this.end);//从文件中获取当前分片数据
let fileReader = new FileReader();
//异步读取本地文件分片数据并转化为base64字符串
fileReader.readAsDataURL(fileShardtem);
//本地异步读取成功后,进行上传
fileReader.onload = function (e) {
let base64str = e.target.result;
params.append('base64', base64str)
_this.uploadFile(params)
}
let perentNum=Math.ceil(this.fileShardIndex * 100 / this.fileShardTotal)
if(perentNum>100){
this.percentage=100
}else{
this.percentage=perentNum
}
}
}else if(res.data.code==100){
var fileId= res.data.id
//上传完成
_this.percentage=100
_this.switchC=true
_this.finishUpload(fileId)
}
console.log(this.percentage)
}).catch((error)=>{
if(error.response){
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
}else{
console.log(error.message)
}
})
},
updateProgress(file,params){
var _this= this
var param = new URLSearchParams()
param.append('shardKey', _this.fileKey)
// 批量上传 请求上边的checkProgress接口
checkProgress(param).then(res => {
if(res.data.code==200){
//新文件
_this.start=(_this.fileShardIndex-1)*_this.fileShardSize;
_this.end =Math.min(file.size,_this.start+_this.fileShardSize);
_this.fileShard=file.slice(_this.start,_this.end);//从文件中获取当前分片数据
}else if(res.data.code==220){
_this.fileShardIndex=res.data.ShardIndex;
//有上传未完成的
_this.start=(_this.fileShardIndex-1)*_this.fileShardSize;
_this.end =Math.min(file.size,_this.start+_this.fileShardSize);
_this.fileShard=file.slice(_this.start,_this.end);//从文件中获取当前分片数据
}else if (res.data.code==240){
//急速上传
var fileId= res.data.id
_this.percentage=100
_this.switchC=true
console.log(this.percentage)
// this.$message({
// message: '极速上传成功',
// type: 'success'
// })
_this.finishUpload(fileId)
return false;
}
//读取base64str
let fileReader = new FileReader();
//异步读取本地文件分片并转化为base64字符串
fileReader.readAsDataURL(_this.fileShard);
//本地异步读取成功,进行上传
fileReader.onload=function (e){
let base64str=e.target.result;
params.append('base64', base64str)
params.append('fileShardIndex', _this.fileShardIndex)
if(_this.switchC==false){
_this.uploadFile(params)
}
}
}).catch((error)=>{
this.$message.error('上传错误')
})
},
finishUpload(fileId){
var _this=this
//进行保存提醒
_this.uploadMsg = _this.$message({
duration:0,
message: "请稍等,正在保存...",
type: "warning"
});
// 上边文件上传成功后的接口
finishUploadPackage(fileId).then(res => {
if(res.data.code==100){
let uploadFileName= res.data.data.fileName
let uploadFilePath= res.data.data.filePath
_this.fileList = []
if(uploadFileName != null && uploadFileName.length > 0){
var fileObj = { name: uploadFileName , filePath:uploadFilePath}
_this.fileList.push(fileObj)
}
//关闭消息提醒
_this.uploadMsg.close()
//上传完成提示
_this.$message({
duration:2000,
message: '上传已完成',
type: 'success'
})
_this.showUploadProcess=false
_this.showUploadBtn=true
}
})
},
5. 文件上传,参考:组件 | Element
<el-upload
class="upload-demo inline-block margin-left-10 margin-right-10"
ref="elUpload"
action="#"
:show-file-list="false"
multiple
:limit="1"
:before-upload="onBeforeUpload"
>
<!-- before-upload:上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
<el-button size="min" icon="el-icon-upload" type="primary" style="" v-show="showUploadBtn">批量上传</el-button>
<el-progress style="width:350px;margin-top:50px;margin-left:10px" v-show="showUploadProcess" color="green" type="line" :text-inside="true" :stroke-width="20" :percentage="percentage"></el-progress>
</el-upload>
<el-upload
ref="elUploadResult"
action="#"
:show-file-list="true"
:file-list="fileList"
:limit="1"
:before-remove="handleBeforeRemove"
:on-remove="handleRemove"
>
</el-upload>