最近用vue做的项目中有用到一个图片上传,实时预览的功能;其中我用到了两个HTML5的新特性:FileReader和FormData;
首先介绍下这两个方法:
HTML5定义了FileReader作为文件API的重要成员用于读取文件,根据W3C的定义,FileReader接口提供了读取文件的方法和包含读取文件结果的各类事件模型,想查看详细描述,请阅读FileReader。
1.检测浏览器对FileReader兼容性的方法:
if(window.FileReader) {
var fr = new FileReader();
// add your code here
}
else {
alert("Not supported by your browser!");
}
/*方法二:检测FileReader类型*/
if(typeof FileReader==='undefined'){
alert('您的浏览器不支持图片上传,请升级您的浏览器');
return false;
}
2.调用fileReader对象的方法
FileReader实例拥有四个方法,其中三个是用来读取文件,另一个是用来中断读取的。需要注意的是,无论读取成功或是失败,方法并不会返回读取结果,这一结果(储存在result属性中)要用FileReader处理事件去获取;
方法名 | 参数 | 描述 |
---|---|---|
abort | none | 中断读取 |
readAsBinaryString | file | 将文件转化为二进制码 |
readAsDataURL | file | 将文件读取为DataURL |
readAsText | file,[encoding] | 将文件读取为文本 |
readAsText:该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8。这个方法非常容易理解,将文件以文本方式读取,读取的结果即是这个文本文件中的内容。
readAsBinaryString:该方法将文件读取为二进制字符串,通常我们将它传送到后端,后端可以通过这段字符串存储文件。
readAsDataURL:这是例子程序中用到的方法,该方法将文件读取为一段以 data: 开头的字符串,这段字符串的实质就是 Data URL,Data URL是一种将小文件直接嵌入文档的方案。这里的小文件通常是指图像与 html 等格式的文件。
3.处理事件
FileReader 包含了一整套完成的事件模型,用于捕获读取文件时的状态,下面这个表格归纳了这些事件。
事件 | 描述 |
---|---|
onabort | 中断时触发 |
onerror | 出错时触发 |
onload | 文件读取成功完成时触发 |
onloadend | 读取完成时触发,无论读取成功或失败 |
onloadstart | 读取开始时触发 |
onprogress | 读取中 |
XMLHttpRequest Level 2添加了一个新的接口 - - - FormData 对象,我们可以通过 javascript 用一些键值对来模拟表单提交,我们还可以用XMLHttpRequest 的 send() 方法来异步的提交表单。与普通的 Ajax 相比,使用 FormData 的最大优点就是我们可以异步上传二进制文件。
创建一个FormData 对象
你可以创建一个空的FormData对象,然后使用append()方法想该对象添加字段,如下:
var oMyForm = new FormData();
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // 数字123456被立即转换成字符串"123456"
// fileInputElement中已经包含了用户所选择的文件
oMyForm.append("userfile", fileInputElement.files[0]);
var oFileBody = "<a id="a"><b id="b">hey!</b></a>"; // Blob对象包含的文件内容
var oBlob = new Blob([oFileBody], { type: "text/xml"});
oMyForm.append("webmasterfile", oBlob);
var oReq = new XMLHttpRequest();
oReq.open("POST", "http://foo.com/submitform.php");
oReq.send(oMyForm);
注:字段 “userfile” 和 “webmasterfile” 的值都包含了一个文件。通过 FormData.append() 方法赋给字段 “accountnum” 的数字被自动转换为字符(字段的值可以是一个 Blob 对象,File对象或者字符串,剩下其他类型的值都会被自动转换成字符串)。
在该例子中,我们创建了一个名为 oMyForm 的 FormData 对象,该对象中包含了名为”username”,”accountnum”,”userfile” 以及 “webmasterfile” 的字段名,然后使用XMLHttpRequest的 send() 方法把这些数据发送了出去。”webmasterfile” 字段的值不是一个字符串,还是一个 Blob 对象。
使用form表单初始化一个FormData对象
可以用一个已有的 form 元素来初始化 FormData 对象,只需要把这个 form 元素作为参数传入 FormData 构造函数即可:
var formElement = document.getElementById("myFormElement");
var oReq = new XMLHttpRequest();
oReq.open("POST", "submitform.php");
oReq.send(new FormData(formElement));
浏览器兼容性
Feature | Chrome | Firefox(Gecko) | IE | Opera | Safari |
---|---|---|---|---|---|
Basic support | 7+ | 4.0(2.0) | 10+ | 12+ | 5+ |
支持filename参数 | (yes) | 22.0(22.0) | ? | ? | ? |
下面是我的项目代码:
<div class="issue-project-main clearfix" id='issue_project_main'>
<form v-on:submit.prevent="submit_issue_project()" id="project_form">
<div class="issue-project-left">
<div v-if="images.length >0">
<ul>
<li v-for="(image,key) in images" style="position:relative;">
<img :src="image" @click='delImage(key)' class="image-upload"/>
<a href="#" class="remove-box" @click='delImage(key)'>
<span class="image-remove"></span>
</a>
</li>
</ul>
<!-- <button @click="removeImage">移除全部图片</button> -->
<!-- <button @click='uploadImage' >上传</button> -->
</div>
<div v-show="showbutton">
<a id='addPic' href="" v-on:click="addPic">上传项目图片 </a>
<input type="file" id="imagebox" v-on:change="getImage()" @change="onFileChange" name="image" style="display: none;">
</div>
</div>
<div class="issue-project-form-list issue-project-form-btn">
<p class='errormsg' v-text='errormsg'></p>
<div class="mask-submit" v-if='disabled'></div>
<input type="submit" class="issue-project-btn" value="完成"/>
<input type="button" class="cancel-project-btn" value="取消" @click='back' />
</div>
</div>
</form>
</div>
js代码:
var issue_project = new Vue({
el:'#issue_project_main',
data:{
showbutton:true,
disabled:false,
url_ajax:'',
images: []//显示的图片
},
watch:{
images:function(){
if(this.images.length >0){
this.showbutton = false
}else{
this.showbutton = true
}
}
},
methods: {
/*以下是上传图片的js*/
getImage:function(){
file = $("#imagebox")[0].value
},
addPic:function(e){
e.preventDefault();
$('input[type=file]').trigger('click');
return false;
},
onFileChange:function(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)return;
this.createImage(files);
},
createImage:function(file) {
if(typeof FileReader==='undefined'){
alert('您的浏览器不支持图片上传,请升级您的浏览器');
return false;
}
var image = new Image();
var vm = this;
var leng=file.length;
for(var i=0;i<leng;i++){
var reader = new FileReader();
reader.readAsDataURL(file[i]);
reader.onload =function(e){
vm.images.push(e.target.result);
};
}
},
delImage:function(index){
this.images.shift(index);
},
removeImage: function(e) {
this.images = [];
},
/*以上是上传图片的js*/
submit_issue_project:function(){
var _this = this;
_this.url_ajax = '/vdg/api/project/create'
if(edit){
_this.url_ajax = '/vdg/api/project/update'
}
var formElement = document.getElementById("project_form");
var formData = new FormData(formElement);
axios({
method: 'post',
url: _this.url_ajax,
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
}).then(function(res){
console.log(res);
}).catch(function(error) {
console.log(error);
});
}
},
})