先记录下,后面有时间再去实现
前端slice分片上传,后端用表记录分片索引和分片大小和分片总数,当接受完最后一个分片(分片索引等于分片总数,分片索引从1开始),就合并分片成完成的文件。前端需要递归上传,并显示加载动画和根据分片完成数量显示进度条
临时demo
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<form action="http://www.baidu.com/a">
<input type="file" type="hidden" id="file">
<button type="button" id="btn">触发上传button>button>
form>
body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js">script>
<script>
/* 监听选择图片的事件 */
document.querySelector('#file').onchange = (e)=>{
console.log('改变了');
console.log(this); // 这里的this变成了Window, 因为写成了箭头函数。
console.dir(e.target);
// 选择了一个文件,所以数组只有一个元素
console.log(e.target.files); // FileList {0: File, length: 1}
console.log(e.target.files[0]); // File {name: 'GIF 2023-4-1 18-14-01.gif', lastModified: 1680344051705, lastModifiedDate: Sat Apr 01 2023 18:14:11 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 242914, …}
upload(e.target.files[0])
document.querySelector('#file').value = '' // 让下次即使选择同一个文件仍能触发onchange事件
}
function upload(file) {
console.log(file instanceof Blob); // true, 而Blob中有个slice方法,可以对文件进行分片
let formData = new FormData()
let shardSize = 10 * 1024 * 1024
let shardIndex = 1
let start = shardSize * shardIndex
let end = Math.min(file.size, start + shardSize)
console.log(start,end);
formData.append('mfile', file.slice(start,end))
// 携带数据请求后台
$.ajax({
url: 'http://127.0.0.1:8083/article/uploadImg',
type: 'POST',
data: formData,
contentType: false,
processData: false,
cache: false,
success: function (data) {
if (data.success) {
alert('添加成功');
} else {
alert('添加失败');
}
}
});
}
/* 点的是#btn,但是我们要触发#file文件上传 */
document.querySelector('#btn').onclick = function(){
document.querySelector('#file').click()
}
script>
html>
@PostMapping("uploadImg")
public Result uploadImg(@RequestParam("mfile") MultipartFile mfile) throws IOException {
String filename = mfile.getOriginalFilename();
mfile.transferTo(new File("D:\\Projects\\vue-springboot\\src\\main\\resources\\static\\img\\"+filename));
return Result.ok(filename);
}
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
public static void main(String[] args) throws IOException {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-shenzhen.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "xxx";
String accessKeySecret = "yyy";
// 填写Bucket名称,例如examplebucket。
String bucketName = "test-zzhua";
String objectName = "video/juc.mp4";
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ObjectMetadata meta = new ObjectMetadata();
meta.setObjectAcl(CannedAccessControlList.PublicRead);
RandomAccessFile raFile = new RandomAccessFile(new File("D:\\Projects\\vue-springboot\\src\\main\\resources\\static\\img\\juc.mp4"), "r");
long totalLen = raFile.length();
// 定义每次追加上传的大小 3M
long everyLen = 3 * 1024 * 1024;
long accLen = 0;
byte[] bytes = new byte[5 * 1024]; // 缓冲数组5k
while (true) {
// 找到上次读取的位置
raFile.seek(accLen);
boolean finish = false;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 当前读取累积3M, 或不够3M就读完了
int currLen = 0;
while (true) {
int readLen = raFile.read(bytes);
if (readLen == -1) {
finish = true;
break;
}
currLen += readLen;
baos.write(bytes, 0, readLen);
if (currLen >= everyLen) {
break;
}
}
// 发起追加请求
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
AppendObjectRequest appendObjectRequest = new AppendObjectRequest(bucketName, objectName, bais,meta);
appendObjectRequest.setPosition(accLen);
ossClient.appendObject(appendObjectRequest);
if (finish) {
break;
}
accLen += currLen;
}
}
参考:SpringBoot大文件上传–前端计算文件的MD5
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.2/spark-md5.js">script>
head>
<body>
<form id="from" method="post" action="/upload" enctype="multipart/form-data">
<table>
<tr>
<td>
<input id="md5" name="md5">
<input id="file" name="upload" type="file">
<input id="submit" type="submit" value="上传">
td>
tr>
table>
form>
body>
<script>
//注意此方法引用了SparkMD5库 library:https://github.com/satazor/SparkMD5
//监听文本框变化
document.getElementById("file").addEventListener("change", function() {
//声明必要的变量
chunks=0;
currentChunk=0;
var fileReader = new FileReader();//一个用来读取文件的对象
//文件分割方法(注意兼容性)
blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice,
file = document.getElementById("file").files[0],
//文件每块分割2M,计算分割详情
chunkSize = 2097152,
chunks = Math.ceil(file.size / chunkSize),//文件分成了几块
currentChunk = 0,//当前处理的第几块
spark = new SparkMD5();//创建md5对象(基于SparkMD5)
//每块文件读取完毕之后的处理
fileReader.onload = function(e) {
console.log("读取文件", currentChunk + 1, "/", chunks);
//每块交由sparkMD5进行计算
spark.appendBinary(e.target.result);
currentChunk++;
//如果文件处理完成计算MD5,如果还有分片继续处理
if (currentChunk < chunks) {
loadNext();
} else {
md5=spark.end();//最终的MD5
console.log("MD5:"+md5);
}
};
//处理单片文件的上传
function loadNext() {
var start = currentChunk * chunkSize,
end = start + chunkSize >= file.size ? file.size : start + chunkSize;
fileReader.readAsBinaryString(blobSlice.call(file, start, end));
//blobSlice.call(file, start, end)每次执行到blobSlice的时候就会跳转到blobSlice定义的地方,可以理解为一个循环
}
loadNext();
});
script>
html>
参考:详解JAVA中获取文件MD5值的四种方法
须引入commons-codec包
String s = DigestUtils.md5Hex(new FileInputStream(new File("D:\\documents\\尚硅谷谷粒学院项目视频教程\\6 - What If I Want to Move Faster.mp4")));
ystem.out.println(s);