在开发过程中,不管怎样简单的需求,在量级
达到一定层次时,都会变得异常复杂。
就拿今天要说的文件上传来说,文件上传简单,但是当文件大小变得太大超出控制时,就会变得复杂了!
当上传大文件时,会存在以下几个变量会影响我们的用户体验:
为了解决上述问题,我们需要对大文件上传单独处理
这里涉及到切片上传
及断点续传
两个概念
顾名思义,切片上传,就是将所要上传的大文件,按照一定的大小,将整个文件切割成多个数据块(Part
)来进行分片上传。上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。
大致流程如下:
断点续传,指的是在下载或上传时,将下载或上传任务人为的划分为几个部分
每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度。现在常用的网盘下载工具和云存储平台大多采用了这种方式,可由用户手动暂停或继续文件上传、下载等操作。
一般实现方式有两种:
上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可
如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可。不过反复中断,小概率导致文件合并后,数据乱码的情况出现。
下面的内容都是伪代码(仅提供思路参考)
1. 读取文件内容:
//
const input = document.querySelector('input')
input.addEventListener('change', function() {
var file = this.files[0]
});
2. 可以使用md5, 实现文件的唯一性加密
const md5Code = md5(file)
3. 将文件进行切割,分成若干“块”小文件
// 创建一个reader对象, 允许操作file或blob
var reader = new FileReader()
// 用于启动读取指定的 Blob 或 File 内容
reader.readAsArrayBuffer(file)
// 当文件成功读取时,执行load 事件
reader.addEventListener("load", (e) => {
//每10M切割一段,这里只做一个切割演示,实际切割需要根据实际文件大小,指定循环切割的片数和每片的大小...
var slice = e.target.result.slice(0, 10*1024*1024)
});
FileReader相关理论知识 (点击跳转)
4.将切片逐片上传
const formData = new FormData();
// 与后端协商,看看如何界定上传片数。
// 如上面流程图所述,可以在第一片(尽量切小片一点,降低出错率)上传的时候,携带这个大文件对应的信息,如:整体裁切片数,文件总大小,文件名称及文件类型等等
//这里是有一个坑的,部分设备无法获取文件名称和文件类型,可以通过读取文件二进制流,解析文件类型,具体可以通过百度了解,不展开叙述。主要讲思路
formData.append('file-' + index, slice) // index 代表 此片的序号
formData.append('fileName', file.filename)
formData.append('fileType', file.fileType)
formData.append('md5Code', md5Code)
// 》XMLHttp
var xhr = new XMLHttpRequest()
xhr.addEventListener('load', function() {
//xhr.responseText
})
xhr.open('POST', '')
xhr.send(formData)
// 监听
xhr.addEventListener('progress', updateProgress)
xhr.upload.addEventListener('progress', updateProgress)
function updateProgress(event) {
if (event.lengthComputable) {
//进度条
}
}
// 》 ajax
$.ajax({
type:"post",
url:"http://XXXXX",
async:true,
data:formData,
cache:false,
processData: false,
contentType: false,
xhr: function xhr() {
//获取原生的xhr对象
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
//添加 progress 事件监听
xhr.upload.addEventListener('progress', function (e) {
//e.loaded 已上传文件字节数
//e.total 文件总字节数
var percentage = parseInt(e.loaded / e.total * 100)
}, false)
}
return xhr
},
success:function(res){
alert(JSON.stringify(res))
}
})
// 》 axios
let config = {
headers:{
'Content-Type':'multipart/form-data',
},
transformRequest: [function (data) {
return data
}],
onUploadProgress: progressEvent => {
//上传进度百分比
let persent = (progressEvent.loaded / progressEvent.total * 100 | 0)
console.log(persent)
},
}
axios.post(
'http://xxxxxxxx/video/upload',
formData,
config
).then(response=>{
var result = response.data
if(result.status == 0){
console.log(result)
}else{
this.$message({
message: '上传失败',
type: 'error',
duration:'1000'
})
}
}).catch(err => {
console.log(err)
})
说明:e.loaded 为已上传文件字节数,e.total 为文件总字节数,可通过计算得出已上传字节数占比,读者可根据自己项目需要使用进度条或进度环
有了切割上传后,也就有了文件唯一标识信息,断点续传变成了后台的一个小小的逻辑判断
后端主要做的内容为:根据前端传给后台的md5值,到服务器磁盘查找是否有之前未完成的文件合并信息(也就是未完成的半成品文件切片),取到之后根据上传切片的数量,通过接口返回,告诉前端开始从第几节上传,继续进行上次未完成的文件上传
如果想要暂停切片的上传,可以使用XMLHttpRequest 的 abort 方法,其他请求同样有对应的暂停方法,可百度了解。
js实现文件切片上传,断点续传
百度冲浪
提供自掘金大佬:前端切片上传文件(可暂停),以链接形式返回
< 每日技巧: JavaScript代码优化 >
< 每日知识点:关于Javascript 精进小妙招 ( Js技巧 ) >
<Javascript技巧: Javascript 是个难泡的妞,学点技巧征服 “ 她 ” >
< 在element-ui中: 使用el-tree + el-table组件,联动请求用户数据表格组件 (基础版,后续可能更新) >