上篇文章咱们介绍了大文件切片上传的原理,但是在传输过程中难免出现切片丢失的情况,传输过程中网速卡顿,服务器链接超时,等等都会造成切片信息的丢失,那如何避免文件切片信息丢失呢?
基本思路是,首先我们要计算出文件的MD5值,将MD5值和文件一起传递到服务器,服务器接收到文件读取文件的MD5值,然后跟前端传递的MD5进行比对,相同则文件数据未丢失,不相同证明文件信息丢失。
那什么是MD5呢?英文全称为Message Digest Algorithm MD5(中文名为消息摘要算法第五版),它是计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护,以确保信息传输完整一致。
前端读取文件的MD5值需要用到一个库https://github.com/satazor/js-spark-md5,这个库读取文件MD5值时,需要读取文件的buffer数据,而读取文件的buffer数据需要用到html5的文件读取接口fileReader api。所以前端读取文件MD5值的核心技术是js-spark-md5和fileReader api。
将文件切片上传,并且将文件的MD5读取出来后一起发送到后端,代码如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件切片上传</title>
<script src="http://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="./uuid.js"></script>
<script src="./js-spark-md5.js"></script>
</head>
<body>
<form action="/upload2" method="post" enctype="multipart/form-data">
<input name="test" type="file" id="img">
</form>
</body>
<script>
function p(formdata) {
return new Promise(function (resolve, reject) {
$.ajax({
url: "/upload2",
type: "POST",
data: formdata, //刚刚构建的form数据对象
async: true, //异步
processData: false, //很重要,告诉jquery不要对form进行处理
contentType: false, //很重要,指定为false才能形成正确的Content-Type
success: function (data) {
resolve(data);
}
})
})
}
$(function () {
// 1.实例化文件读取方法对象
var reader = new FileReader();
// 2.实例化读取buffer MD5方法
var spark = new SparkMD5.ArrayBuffer();
var input = document.querySelector('input');
// 3.监听上传文件input的change事件
input.addEventListener('change', function () {
//4. 获取文件blob信息
var file = this.files[0];
console.log(file);
// 5.将blob读取为buffer信息,这一步是异步操作
reader.readAsArrayBuffer(file);
// reader.readAsBinaryString(file);
});
// 6.监听读取blob信息完成事件
reader.addEventListener("load", function (e) {
var chunksize = 1 * 1024 * 1024;
// 7、将buffer信息转化为blob,为了传输
var blob = new Blob([e.target.result]);
console.log(blob.toString())
///8、用spark直接读取文件的MD5值,
spark.append(e.target.result);
///9、读取完成,用一个变量来接收
let SparkMD5 = spark.end();
console.log(SparkMD5)
var count = Math.ceil(blob.size / chunksize);
var uuidfolder = uuidv1();
var arr = []
async function main() {
for (let i = 0; i < count; i++) {
var slice = blob.slice(i * chunksize, (i + 1) * chunksize);
var formdata = new FormData();
formdata.append('imgname', uuidfolder);
formdata.append('imgorder', i);
formdata.append('img', slice);
var a = await p(formdata);
arr.push(a);
}
}
main().then(function () {
//10、 将md5值发送到服务器端。
$.post('/merge2',{id:uuidfolder,spark:SparkMD5},function(data){
console.log(data);
})
})
});
})
</script>
</html>
代码详解见注释,主要的是注意FileReader和 SparkMD5的运用。
服务器接收到前端发送的数据后,将切片拼接为一个完整文件,然后读取该文件的MD5值,和前端传过来的MD5值进行比对,如果相等证明切片未丢失,如果不相等,证明切片丢失。代码如下:
router.post('/merge2',function(req,res){
let id = req.body.id;
let spark = req.body.spark;
let folderpath = path.join(__dirname,"..",'public/uploads',id);
let destinpath = path.join(__dirname,"..",'public/img',id+'.jpg');
let dist = '/img/'+id+'.jpg'
fs.readdir(folderpath,function(err,arr){
let arr2 = arr.map(e=>path.join(folderpath,e));
concat(arr2, destinpath, function(err) {
// fs.readFile(destinpath,function(err,data){
// if (err) throw err
// res.send(dist);
// })
// 1、此处使用了两个库来读取文件的MD5值
// 分别是md5和spark-md5(和前端用的同样的库)
var md5sum = crypto.createHash('md5');
var frontspark = new SparkMD5.ArrayBuffer();
// 2、由于读取文件的MD5值非常耗时,
// 所以这里应用了文件的流模式读取文件的MD5值
var stream = fs.createReadStream(destinpath);
stream.on('data', function(chunk) {
md5sum.update(chunk);
frontspark.append(chunk)
});
stream.on('end', function() {
str = md5sum.digest('hex');
str2 = frontspark.end()
if (err) throw err
console.log(str,spark,str2);
if(str = spark){
res.send(dist);
}
});
});
})
})
项目源码地址:https://github.com/clm1100