HTML5 大文件断点续传完整思路整理
用 html5 的新特性分割文件,为达到断点续传功能
用 spark.js 获取文件md5以确保文件的唯一性
流程概述:
复制代码
(此功能前端共需调用3个接口,分别为简称作 A / B / C)
1,获取文件信息:使用HTML5的原生上传input,选择文件后,获取文件的所有信息(文件名、文件总字节数等)
2,计算总切片:跟后台约定好单个切片大小,比如1M/片,计算文件总大小/单个切片大小=总切片数
3,计算文件MD5和每个切片的MD5:引用spark-md5.min.js来生成MD5,此js的调用可以获取文件MD5、切片的MD5和切片的数据
4,调用A接口查询从第几个切片开始上传:需要向后台传入的关键参数是文件名、文件总大小、文件的MD5
5,获取到初始上传切片位置,正式开始分片上传到服务器:从A接口获取到切片位置后,调用接口B,将切片的MD5和切片数据传给后台,此后循环调用接口B,直到最后一切片上传结束
6,所有切片上传结束后,调用接口C将文件存库
详细代码展示:
HTML:
复制代码
<input type='file' title="" accept=".mp4"
name='myfiles' id="mediaFile" class="" onchange="handleFile()" />
获取文件信息:
复制代码
function handleFile() {
var fileInputs = $("input[name='myfiles']")[0]; //获取input里的文件信息
//切割文件的每条内容分别存放
var name = fileInputs.files[0].name, //文件名
size = fileInputs.files[0].size, //文件总字节
type = fileInputs.files[0].type, //文件类型 (此功能暂且用不到)
shardSize = 1024 * 1024, // 每个切片的总字节数,比如此时以1M为一个分片
shardCount = Math.ceil(size / shardSize); //总片数
}
计算整个文件的MD5:(前提是引入了spark.js,下载地址: js-spark-md5)
复制代码
var singleFileData = new Array(); //初始空数组,储存所有的切片数据
var fileReader = new FileReader(),
blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice,
chunkSize = shardSize , //单个切片的总字节数
chunks = shardCount, //总片数
currentChunk = 0;
var spark = new SparkMD5.ArrayBuffer();
fileReader.onload = function (e) {
//*******获取分片文件,用于计算所有切片的MD5***
for (var i = 0; i < chunks; i++) {
var start = i * chunkSize,
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
var sliceFile = blobSlice.call(file, start, end);
// 调用此方法,计算每个切片的MD5
getChunkMd5(sliceFile, i);
}
//*****************************
spark.append(e.target.result);
if (currentChunk < chunks) {
currentChunk++;
loadNext();
} else {
var allFileEnd = spark.end()
console.log("此值为整个切片的MD5", allFileEnd)
//真实MD5值 :allFileEnd=a0ce27800ee7d948422f3fe16e898f22
// ***调用接口(1),查询从第几个切片开始上传***
// ***在此处写接口A的调用方法***
}
};
function loadNext() {
//计算切片的start,end,用于切割出切片的数据
var start = currentChunk * chunkSize,
end = start + chunkSize >= file.size ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
//储存切片的数据
//判断如果singleFileData小于总切片数,则继续往数组添加切片数据
if (singleFileData.length != chunks) {
singleFileData.push(blobSlice.call(file, start, end)) //将每个切片push到空数组里
}
};
//调用此方法,循环获取切片数据
loadNext()
计算切片的MD5:
(此步骤其实可以省略,但为了严谨后端需要根据前端传入的切片数据计算出MD5,跟前端传入的MD5对比)
复制代码
var mySingleFileMd5 = []; //用于储存所有切片的MD5
function getChunkMd5(file, i) {
var spark = new SparkMD5.ArrayBuffer();
var fileReader = new FileReader();
fileReader.onload = function (e) {
spark.append(e.target.result);
mySingleFileMd5[i] = spark.end();// 将每个切片的MD5存起来
console.log("所有切片的MD5数组=", mySingleFileMd5);
}
fileReader.onerror = function () {
console.warn('oops, something went wrong');
}
function loadNext() {
fileReader.readAsArrayBuffer(blobSlice.call(file, 0, file.size));
}
loadNext();
}
调用接口A后获取到 PARTINDEX(从第几个切片开始上传),接着调用接口B进行上传,上传后手动计算进度。
A接口传参、取参如下:
复制代码
var params = {
serviceid: 'wcm61_bigfile',
methodname: 'startUpload',
fileName: 手机QQ视频_20181212094958.mp4,
fileDigest: 65309fc9684b4eb2f3d281da5ad17b6e,
fileSize: 100904886,
};
{
"MSG":"操作成功",
"DATA":{
"TOTALPARTCOUNT":"49",
"PARTINDEX":"1", //此字段就是后台告诉前端从第几个切片开始上传
"PARTSIZE":"2097152",
"FLAG":"1",
"FILENAME":"手机QQ视频_20181212094958.mp4"
},
"ISSUCCESS":"true"
}
B接口传参、取参如下:
复制代码
var data = singleFileData[partIndex - 1] //切片数据
var fileName =手机QQ视频_20181212094958.mp4; //文件名
var fileDigest = 65309fc9684b4eb2f3d281da5ad17b6e; //文件的MD5
var partIndex = 1; //切片位置
var partDigest = ea58e21870ad4c22f2c75645564654b2; //切片的MD5
{
"filename":"手机QQ视频_20181212094958.mp4",
"PARTINDEX":"2", //下次开始传第2个切片
"PARTSIZE":"2097152", //切片大小
"totalPartCount":"49" //总切片数
"ISSUCCESS":"1"
}
通过B接口,不断将切片上传到服务器,并且实时计算上传进度
复制代码
var processNum =
parseInt((PARTINDEX - 1) / totalPartCount * 100) // 已上传的切片/总切片数
最终实现静态效果和接口如下:
参考文章:http://blog.ncmem.com/wordpress/2023/10/14/html5-%e5%a4%a7%e6%96%87%e4%bb%b6%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0%e5%ae%8c%e6%95%b4%e6%80%9d%e8%b7%af%e6%95%b4%e7%90%86-2/
欢迎入群一起讨论