相信大家在工作中也会遇到前端文件上传的需求,虽然已经在项目中使用FormData和elementUI中upload组件都实现过类似上传效果,但自己对这块一直一知半解,因此做一个笔记梳理一下前端方面的文件上传操作,以供日常参考。
总体来说常用的两种方式:二进制传输和base64格式直接传输
正文开始之前先简单认识一下文件上传的四个相关对象,以便后续阅读代码更直观:
1、认识文件上传的四个相关对象
1.files对象:
可以通过指定
input
标签
type
属性为
file
来读取files对象,是一个由一个或多个文件对象组成的数组。同时也是
blob
对象的子类,继承了一些
blob
对象的方法
2.blob对象:
表示二进制类型的大对象。在数据库管理系统中,将二进制数据存储为一个单一个体的集合。Blob 通常是影像、声音或多媒体文件。在 JavaScript 中 Blob 类型的对象表示不可变的类似文件对象的原始数据, 使用构造函数创建。
3.formData对象:
FormData就是 XMLHttpRequest Level 2 新增的一个对象,利用它来提交表单、模拟表单提交,最大的优势就是可以上传二进制文件。
作用1:模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式。
作用2:异步上传二进制文件。
4.fileReader对象
构造函数方式实例化一个fileReader对象,readAs()方法将文件对象读取成base64格式或者文本格式
2、二进制blob对象传输
上传时将文件转为blob对象,通过FormData对象搭载传递
使用vue-cli搭建了一个基础框架,代码如下
2.1文件上传前的判断操作
<div>
<p>file组件</p>
<input type="file" @change="fileChange" multiple>
<button @click="submit">提交</button>
</div>
</template>
<script>
export default {
data(){
return{
fileList:[],
imgsrc:'',
precent:0
}
},
methods:{
//files对象的第一个应用:文件上传之前的大小和类型判断
fileChange(e){
console.log(e.target.files);//一个由不同文件对象组成的对象
let files = e.target.files;
//文件大小判断和文件类型判断 //假设不能大于10M
if(files[0].size > 10*1024*1024){
alert('文件不能大于10M')
}else if(files[0].type != "application/pdf"){
alert("文件类型必须是pdf格式")
}
},
async submit(){
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
console.log(e.target.files)打印结构如下:
由此可见其是一个由多个文件对象组成的对象,每个文件对象内部又有多个属性,比较常用size和type属性做文件上传之前的大小和类型判断。
2.2files与blob转换 缩略图和文本预览
files
对象与
blob
对象的相互转换和
blob
对象的切割方法 :项目中实现缩略图和文本预览
methods:{
fileChange(e){
let files = e.target.files[0];
let _newBlob = new Blob([files]);//将files对象转为blob对象,第一个参数必须是数组
console.log(_newBlob);
//slice()方法 : 切割blob对象,从XX位到XX位
let _sliceBlob = _newBlob.slice(0,5000);
console.log(_sliceBlob);
//将切割后的blob对象转为files对象
let _sliceFile = new File([_sliceBlob],'test.jpg')
console.log(_sliceFile);
//fileReader对象 将文件异步方式转为base64格式或文本格式
// fr.readAsDataURL() 转换为base64格式
// fr.readAsText() 转换为文本格式
let fr = new FileReader();//实例化FileReader对象
fr.readAsDataURL(_sliceFile)
// fr.readAsDataURL(files)
//FileReader对象的readAs方法是异步的,只能通过onload事件监听获取转换的结果
fr.onload = ()=>{
console.log(fr.result);
//实现文件上传前的缩略图预览
this.imgbase64 = fr.result;
}
},
};
</script>
},
控制台输出如下:
四次输出顺序分别为切割前的
blob
对象、切割后的
blob
对象、转换的files文件对象、读取成base64格式的文件对象
可以看到切割前的
blob
对象与切割后的
blob
对象的大小区别,以及文件读取成base64格式后明显不完整
2.3单文件上传
<template>
<div>
<p>file组件</p>
<input type="file" @change="fileChange" multiple>
<button @click="submit">提交</button>
<img style="width: 50px;" :src=imgbase64 >
</div>
</template>
<script>
let _fileObj;//定义操作formdata对象实现单文件上传的空变量
export default {
data(){
return{
imgbase64:'',
}
},
methods:{
fileChange(e){
let files = e.target.files[0];
_fileObj = files;
let _newBlob = new Blob([files]);//将files对象转为blob对象,第一个参数必须是数组
//fileReader对象 将文件异步方式转为base64格式或文本格式
let fr = new FileReader();
fr.readAsDataURL(_newBlob);
//通过onload事件监听获取转换的结果,箭头函数赋值给onload,不改变this指向,直接操作data函数中定义的数据
//文件缩略图操作
fr.onload = ()=>{
this.imgbase64 = fr.result
}
},
async submit(){
let _formdata = new FormData();
//append() 将上传者信息或者文件添加到_formdata
_formdata.append('user','syb');
_formdata.append('file',_fileObj);
//模拟axios上传请求
let data = await axios.post('//url',_formdata);
}
}
};
</script>
<style scoped>
</style>
2.4多文件上传
将多个文件放到一个数组内,然后循环上传这个数组内的文件对象
如果使用input元素的multiple属性规定可以选择多个文件上传,从用户层面来说并不合适。(因为选择文件的时候要按Ctrl键) 建议使用js拼接input元素获取到的文件对象
<template><div><p>manyFile组件</p><input type="file" @change="fileChange" multiple /><button @click="submit">多选提交</button><img style="width: 50px":src="imgbase64"/><span v-for="item in fileList":key="item.name">{{ item.name }}</span><!-- 使用一个span标签预览要上传的文件名 --></div></template><script>exportdefault{data(){return{fileList:[],//定义空数组存储多个文件imgbase64:"",};},methods:{fileChange(e){//e.target.files;//一个由不同文件对象组成的对象//检测e.target.files是否有多个文件if(e.target.files.length >1){
console.log(e.target.files);this.fileList =[...e.target.files];//this.fileList = this.fileList.concat(e.target.files);}else{this.fileList.push(e.target.files[0])}},asyncsubmit(){//循环fileList,每次都创建一个formdata对象上传 this.fileList.forEach(asyncitem=>{let _formdata =newFormData();
_formdata.append(item.name, item);//模拟axios上传请求let data =await axios.post('//url',_formdata);})},},};</script><style scoped></style>
2.5切片上传
<template><div><p>sliceFile组件</p><input type="file" @change="fileChange"/><button @click="submit">切片提交</button><div>
上传进度:{{ precent }}%</div></div></template><script>import{ test }from"../request/api";let _fileObj;exportdefault{data(){return{precent:0,//上传进度百分比};},methods:{fileChange(e){
_fileObj = e.target.files[0];},asyncsubmit(){let size =2*1024*1024;//定义一次切片为2Mlet fileSize = _fileObj.size;let current =0;//定义当前已经上传的文件部分大小//判断如果已经上传的部分小于文件总大小while(current < fileSize){let _formData =newFormData();//一次添加2M大小的切片 注意添加时的说明一般为文件名,后端接收后按照文件名标识拼接
_formData.append(_fileObj.name,_fileObj.slice(current,current+size))let data =awaittest(_formData);//计算当前上传进度,展示到页面this.precent = Math.ceil((current/fileSize)*100)
current += size;}},},};</script><style scoped></style>
3、base64格式传输
base64格式可以直接传给后端,后端接收后解码进行相应操作后返回
版权归原作者 Moonlightboi 所有, 如有侵权,请联系我们删除。