Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
2013年7月28日,尤雨溪第一次在 GItHub 上为 Vue.js 提交代码;2015年10月26日,Vue.js 1.0.0版本发布;2016年10月1日,Vue.js 2.0发布。
最早的 Vue.js 只做视图层,没有路由, 没有状态管理,也没有官方的构建工具,只有一个库,放到网页里就可以直接用了。
后来,Vue.js 慢慢开始加入了一些官方的辅助工具,比如路由(Router)、状态管理方案(Vuex)和构建工具(Vue-cli)等。此时,Vue.js 的定位是:The Progressive Framework。翻译成中文,就是渐进式框架。
Vue.js2.0 引入了很多特性,比如虚拟 DOM,支持 JSX 和 TypeScript,支持流式服务端渲染,提供了跨平台的能力等。Vue.js 在国内的用户有阿里巴巴、百度、腾讯、新浪、网易、滴滴出行、360、美团等等。
Vue 已是一名前端工程师必备的技能,现在就让我们开始深入学习 Vue.js 内部的核心技术原理吧!
文件上传
在前端中,对于文件上传是一个很常见的功能,我们可以上传图片、文件等到服务器,对于一般的小型的文件, 例如小图片,我们可以选择基于 formdata 进行文件上传,也可以基于 base64 格式去上传,直接发送表单请求即可。
但是对于大文件来说,比如几个G甚至几十G的文件来说,我们如果还是按小文件来处理的话,就显得不是很恰当了。本篇文章就是告诉大家如何实现大文件上传的,包括切片上传、断点续传、进度把控等方面,废话不多说,开始吧。
大文件上传思路
这里先简单说一下整体的思路:
针对文件内容生成的唯一的hash值,作为文件名称。确保相同文件,不同的文件名的情况下,生成的hash值是一样的。
将大文件切成若干个小的分片,并且每一个分片的命名格式为 “文件hash值+当前切片索引值”,方便后端合并这些切片。
上传之前,将当前文件的hash值传递给后台,拿到当前文件在数据库里已经存在的切片,如果有切片已经存在,那么前端就不传递这些了,这就是断点续传的原理。
上传过程中,通过监听当前传递的切片数除以切片总数,获取当前上传进度。当进度条百分百时,自动通知后端将这些文件合并。
上传完成。
下面进行代码展示:
生成文件名
这里我们使用一个第三方库:spark-md5,根据文件二进制流,生成hash值。
在vue中,通过:npm i spark-md5 安装
import SparkMD5 from 'spark-md5'
/**
* @description: 拿到文件的hash值
* @Author: hfunteam
* @param {*} file 文件,File类型
* @return {
* buffer: 文件的二进制流
* hash: 生成的文件hash值
* suffix:文件后缀名
* filename: 文件名称 hash + suffix
* }
*/
function changeBuffer(file: File) {
return new Promise(resolve => {
let fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = ev => {
let buffer: ArrayBuffer = ev.target!.result as ArrayBuffer,
spark = new SparkMD5.ArrayBuffer(),
HASH,
suffix;
spark.append(buffer);
// 拿到文件的hash值
hash = spark.end();
// 匹配文件后缀名
suffix = /\.([a-zA-Z0-9]+)$/.exec(file.name)![1];
resolve({
buffer,
hash,
suffix,
filename: `${HASH}.${suffix}`
});
};
});
};
分片上传+断点续传
// html 部分
// js 部分
import axios from 'axios'
// 监听input按钮事件
document.getElementById('input').addEventListener('change', async function () {
// 获取文件的HASH
let alreadyUploadChunks = [], // 当前已经上传的切片
{
hash,
suffix
} = changeBuffer(file);
// 获取已经上传的切片信息
let data = await axios.get('/upload_already', {
params: {
hash
}
});
// 从后端拿到已经上传的切片列表
alreadyUploadChunks = data.fileList
// 实现文件切片处理 「固定数量 或者 固定大小」
let max = 1024 * 100, // 切片大小
count = Math.ceil(file.size / max), // 切片总数
index = 0, // 当前上传的切片索引值
chunks = []; // 存放切片的数组
if (count > 100) {
max = file.size / 100;
count = 100;
}
// 存放切片,注意此处的切片名称,hash+index+suffix
while (index < count) {
chunks.push({
file: file.slice(index * max, (index + 1) * max),
filename: `${HASH}_${index+1}.${suffix}`
});
index++;
}
// 把每一个切片都上传到服务器上
chunks.forEach(chunk => {
// 这里进行断点续传:已经上传的无需在上传
if (alreadyUploadChunks.length > 0 && alreadyUploadChunks.includes(chunk.filename)) {
// 管控进度条
manageProgress();
return;
}
let formdata = new FormData();
formdata.append('file', chunk.file);
formdata.append('filename', chunk.filename);
instance.post('/upload_chunk', formdata).then(data => {
// 管控进度条
manageProgress();
}).catch(() => {
alert('当前切片上传失败,请您稍后再试');
});
});
})
进度条控制+通知合并
当进度条到达百分之百时,调用通知后台合并切片接口,合并成功,则上传成功。
async function manageProgress(){
// 管控进度
document.getElementById('progress').style.width = `${index/count*100}%`;
// 当所有切片都上传成功,我们合并切片
if (index < count) return;
document.getElementById('progress').style.width = `100%`;
try {
data = await instance.post('/upload_merge', {
hash,
count
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
if (data.code === 0) {
alert(`恭喜您,文件上传成功`);
return;
}
throw data.codeText;
} catch (err) {
alert('切片合并失败,请您稍后再试');
}
};
Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
叶阳辉
HFun 前端攻城狮
往期精彩:
Vue 进阶系列丨Object 的变化侦测
Vue 进阶系列丨Array 的变化侦测
Vue 进阶系列丨虚拟DOM和VNode
Vue 进阶系列丨Patch 和模板编译
Vue 进阶系列丨事件相关的实例方法
Vue 进阶系列丨生命周期相关的实例方法
Vue 进阶系列丨生命周期
Vue 进阶系列丨自定义指令
Vue 进阶系列丨最佳实践
Vue 进阶系列丨Mixin 混入
Vue 进阶系列丨权限控制 addRoute()
Vue 进阶系列丨npm发布vue组件
Vue 进阶系列丨Vuex+TS 代码提示
Vue 进阶系列丨自定义指令实现按钮权限功能
Vue 进阶系列丨Pinia 的基本使用
Vue 进阶系列丨vue2和vue3定义插件的区别
Vue 进阶系列丨vuex持久化
Vue 进阶系列丨webWorker 多线程