记录在项目开发过程中遇到的小问题,积累开发经验,欢迎大家一起留言探讨
1、问题来源
在日常开发工作中,我们不免会遇到前端进行大文件上传的操作,如果碰到网络差或者网络中断的的情况,可想而知,一整个文件全部上传
这种方式是不可取的,所以我们可以断点续传,即分片上传文件。
2、实现思路
将文件分成小文件,按照顺序依次上传,后端接收到数据以后,进行文件追加处理,即使断网,等到网络恢复以后,依然可以从上次未传递完毕的点继续进行传递。
3、前端具体实现
3.1 页面
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<input type="file" name="选择文件" id="uploadFile">
<input type="button" value="上传" id="start" onclick="upload(0)">
<input type="button" value="继续" id="continue" onclick="continueUpload()">
<input type="text" id="show">
</div>
</body>
</html>
3.2 逻辑处理
<script>
// //确定分片大小
var size = (1024 * 1024) * 10;
function upload(index) {
localStorage.setItem("index", 0)
var file = document.getElementById("uploadFile").files[0]; //获取文件
let arr = file.name.split('.'); //获取文件的名字和拓展名
let fname = arr[0]
let ext = arr[1]
var star = index * size; //切片的起点
if (star > file.size) {
document.getElementById("show").value = '100%';
return
};
//slice(起点,终点),Blob中的函数
var bool = file.slice(star, (star + size) > file.size ? file.size : (star + size));
var boolname = fname + "." + ext
var boolfile = new File([bool], boolname)
var form = new FormData()
form.append("index", index)
form.append("size", size)
form.append("name", file.name)
form.append("file", boolfile)
form.append("request_id", localStorage.getItem("request_id") || '')
form.append("file", boolfile);
// 为了演示方便,此处使用的是原生请求,可以根据需求自行修改
var xmt = new XMLHttpRequest();
// 此处是后端接口
xmt.open("post", "http://172.16.13.89:8080/demo/upload", true) //发送请求
xmt.send(form)
xmt.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let result = JSON.parse(this.responseText)
if (result.success) {
// 记录后端回传的 文件标识ID,并回传给后端,便于后端进行文件追加
localStorage.setItem("request_id", result.request_id)
index += 1
upload(index);
// 存储在本地,记录此次传递的是第几片
localStorage.setItem("index", index)
// 计算上传百分比
document.getElementById("show").value = ((star / file.size) * 100).toFixed(4) + '%'; //显示上传的进度
} else {
localStorage.setItem("index", index)
}
}else{
console.log(this.readyState)
}
};
}
// 点击继续上传时,触发这个函数,从上次终端的片数继续进行传递
function continueUpload() {
upload(localStorage.getItem("index") || 0)
}
</script>
4、后端接口实现(Java)
此处是后端大佬挤时间给写的代码,目前没有注释,咱也不好意思说啥,大家将就看一下emmmm
package com.example.upload.controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;
@RestController
@CrossOrigin
public class UploadController {
@RequestMapping("/upload")
public DemoUpload upload(@RequestParam("file") MultipartFile file,
@RequestParam("index") Integer index,
@RequestParam(name = "request_id", required = false) String requestId) {
DemoUpload demoUpload = new DemoUpload();
String fileId = requestId;
if (index == 0) {
//生成一个文件ID
fileId = UUID.randomUUID().toString();
String fileDirPath = "C:\\Users\\ROG\\Downloads\\tmp\\" + fileId;
File fileDir = new File(fileDirPath);
fileDir.mkdir();
}
if (StringUtils.isEmpty(fileId)) {
demoUpload.setSuccess(false);
return demoUpload;
}
String fileName = file.getOriginalFilename();
String tmpFilePath = "C:\\Users\\ROG\\Downloads\\tmp\\" + fileId + "\\" + fileName + ".tmp." + index;
String realFilePath = "C:\\Users\\ROG\\Downloads\\tmp\\" + fileId + "\\" + fileName;
File realDest = new File(realFilePath);
File tmpDest = new File(tmpFilePath);
try {
file.transferTo(tmpDest);
FileInputStream fileInputStream = new FileInputStream(tmpDest);
FileOutputStream fileOutputStream = new FileOutputStream(realDest, true);
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1) {
fileOutputStream.write(buf, 0, readLen);
}
fileOutputStream.close();
fileInputStream.close();
demoUpload.setRequest_id(fileId);
demoUpload.setSuccess(true);
return demoUpload;
} catch (IOException e) {
e.printStackTrace();
}
demoUpload.setSuccess(false);
return demoUpload;
}
}
感谢后端大佬奉献源代码,代码自提:
链接:https://pan.baidu.com/s/1iEQVLpi0QRqgkxzRtYg8ZA
提取码:kgpd