前言:很多項目中都需要用到圖片上傳功能,而其多處使用的要求,為了避免重復造輪子,讓我決定花費一些時間去深入了解,最終封裝了一個vue的圖片上傳組件。現將總結再次,希望有幫助。
Layout
<div class="upload-wraper">
<input type="file" id="upload_ele" multiple="false" accept="image/*" @change="uploadFile()" />
</div>
type=file將類型設置為選擇文件
multiple是否允許文件的多選
accept="image/*" 將文件的類型限制為image類型,*包括所有格式的圖片
change事件當type設置為file后,出發的事件為change,也可通過submit實現
這里布局的話,因為是vue組件所以簡單點,不需要多個input構成form表單,然后通過submit提交,一個input通過change事件來實現上傳
Js
Basic information for uploading files
let oFIle = document.getElementById('upload-ele').files[0];
files是input設置為file后的一個js內置對象。files對象死一個read-only屬性,不可被修改!
打印出oFile后可以看到該文件對象的basic information.如下:
isClosed:false 是否已經結束,可以理解為標簽是否閉合
lastModified:1539602132000最后更改的時間timeStamp
lastModifiedDate:Mon Oct 15 2018 19:15:32 GMT+0800 (CST) {}最后更改時間
name:"D9791645A5DF19D17FD7392A080E7A28.jpg"圖片的名稱
path:"/Users/mac/Documents/D9791645A5DF19D17FD7392A080E7A28.jpg"圖片所在的路徑為本地路徑
Size:38938圖片的大小信息 單位為kb
type:'image/jpeg'圖片的類型
webkitRelativePath:""文件相關的路徑
File Size Processing
大小判斷
(oFile.size / 1024) > 1024
1M = 1024KB
Smaller
let form = new FormData();
form.append('file',oFile);
let xhr = new XMLHttpRequest();
xhr.open('post',url,true);
xhr.timeout = 30 * 1000;
xhr.upload.onprogress = this.progress;
xhr.onload = this.uploadComplete;
xhr.onerror = this.uploadFailed;
xhr.upload.onloadstart = () => {
let date = new Date().getTime();
let initSize = 0;
}
xhr.send(form);
XMLHttpRequest()是js內置對象,可以使用該屬性實現請求頭的處理操作。
xhr.open(); 請求方法: post,url: 服務器接受的地址,true/false 是否異步
xhr.timeout; 設置超時時間,據情況而定
xhr.ontimeout; 超時處理,一般為取消請求
xhr.upload.onprogress; 進程處理 ,上傳文件的進度處理
xhr.onload; 請求成功處理
xhr.onerror; 請求失敗處理
Xhr.upload.onloadstart; 請求開始處理的操作,一般創建時間戳,初始化大小。
xhr.send(); 請求配置完畢,發送請求
這里小於1M的文件是可以直接通過放到formData中,通過配置xhr將圖片對象上傳到oss,在請求成功時拿到圖片的網絡路徑供后,提供給后端。
Larger
why:為什么要對大於1M的圖片進行處理呢?因為文件在大於1M的一般是上傳失敗的。常見的ftp文件上傳時,默認是2M,大於時,會上傳失敗,所以這里的圖片上傳進行了大於1M壓縮的處理。
how:流程與小於1M時十分相似,不過,這里添加了圖片的壓縮處理。
more:一般在較大文件處理時,后端也是可以進行處理的,單獨提供較大圖片的接口。這時,我們就不需要進行壓縮了,只需要在判斷大於1024KB時接口更換為處理較大文件的接口即可。
如何壓縮?
-
通過FileReader對象將文件對象讀取成base64格式。
-
通過Image對象結合canvas畫布將base格式的圖片將一定的比例縮小后重新保存成為base64格式。
-
將base64格式轉換成Blob流后,圖片就可以說是壓縮完成了。
-
剩下的步驟重復formData,XMLHttpRequest即可完成較大圖片的上傳。這里需要注意的是,在formData中防止文件時,因為此時是Blob流,所以防止文件時,需要自定義文件格式,這里的處理是 Blob文件 + 時間戳與.jpg組成。
組件源碼
<template>
<!-- 圖片上傳組件 -->
<div class="upload-wraper">
<input type="file" id="upload-ele" multiple="false" accept="image/*" @change="uploadFile(url,quality,hasApi,BigUrl)">
<toast v-model="total.isShow" type="text">{{total.text}}</toast>
</div>
</template>
<script>
import { Indicator } from 'mint-ui';
import { Toast } from 'vux';
export default {
name: 'uploadImage',
components: {
Indicator,
Toast,
},
props: {
'url': String, //小與1M的api
'quality': Number, //圖片質量
'BigUrl': {
type: String,
default: '',
}, //大於1M圖片的api
'hasApi': {
type: Boolean,
default: false
} //是否對大於1M的圖片單獨分配接口
},
data() {
return {
total: {isShow:false,text:""}
}
},
methods: {
uploadFile(url,quality,hasApi,BigUrl) {
Indicator.open(`上傳中`);
// files是input設置為file后的一個內置對象。files對象是一個只讀屬性,不可被修改。
var oFile = document.getElementById('upload-ele').files[0];
console.log('File Object',oFile);
var form = new FormData();
// 大小判斷 如果大於1M就新型壓縮處理
// console.log('File Size Unit:KB',(oFile.size / 1024))
if((oFile.size / 1024) > 1024) {
if(hasApi) {
form.append('file',oFile);
let xhr = new XMLHttpRequest(); //XMLHttpRequest Object
xhr.open('post',BigUrl,true); // Method: post,url: server receive address,true/false isAsync
xhr.timeout = 30 * 1000; //Timeout one minute;
xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
xhr.upload.onprogress = this.progress; //Progress Function
xhr.onload = this.uploadComplete; //Upload Success Function
xhr.onerror = this.uploadFailed; //Upload Failed Funciton
xhr.upload.onloadstart = () => {
let date = new Date().getTime(); // TimeStamp Prevents Caching
let initSize = 0; // Init File Size Zero
} // Upload Start
xhr.send(form);
} else {
this.imgCompress(oFile,{quality: quality},
(base64Codes) => {
var bl = this.convertBase64UrlToBlob(base64Codes);
form.append("file", bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件對象
console.log(form);
let xhr = new XMLHttpRequest(); // XMLHttpRequest 對象
xhr.open("post", url, true); //post方式,url為服務器請求地址,true 該參數規定請求是否異步處理。
xhr.upload.onprogress = this.progress; //Progress Function
xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
xhr.onload = this.uploadComplete; //Upload Success Function
xhr.onerror = this.uploadFailed; //Upload Failed Funciton
xhr.upload.onloadstart = function() {
let ot = new Date().getTime(); // TimeStamp Prevents Caching
let oloaded = 0; // Init File Size Zero
};// Upload Start
xhr.send(form);
})
}
} else {
// 小與1M
form.append('file',oFile);
let xhr = new XMLHttpRequest(); //XMLHttpRequest Object
xhr.open('post',url,true); // Method: post,url: server receive address,true/false isAsync
xhr.timeout = 30 * 1000; //Timeout one minute;
xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
xhr.upload.onprogress = this.progress; //Progress Function
xhr.onload = this.uploadComplete; //Upload Success Function
xhr.onerror = this.uploadFailed; //Upload Failed Funciton
xhr.upload.onloadstart = () => {
let date = new Date().getTime(); // TimeStamp Prevents Caching
let initSize = 0; // Init File Size Zero
} // Upload Start
xhr.send(form);
}
},
/**
* @description Request Success
*/
uploadComplete(evt) {
let res = JSON.parse(evt.target.responseText);
if(evt.target.readyState == 4 && evt.target.status == 200) {
this.$emit('upload',res.result.url);
} else {
this.uploadFailed();
}
},
/**
* @description Request Failed
*/
uploadFailed(evt) {
Indicator.close();
this.total = {
isShow:true,
text:"上傳失敗"
}
},
/**
* @description Timeout Function
*/
uploadTimeout(evt) {
this.cancleUploadFile(evt)
Indicator.close();
this.total = {
isShow:true,
text:"請求超時"
}
},
/**e
* @description Upload Cancel
*/
cancleUploadFile(evt) {
evt.abort();
},
/**
* @description Requst Loading....
*/
progress(progressEvent) {
if(!progressEvent.lengthComputable) {
this.total = {
isShow:true,
text:"進度讀取失敗"
}
return false;
}
let precent = Math.floor(100 * progressEvent.loaded / progressEvent.total); //Upload Progress
if(precent < 100) {
Indicator.open(`上傳中${precent}%`);
} else {
Indicator.close();
this.total = {
isShow:true,
text:"上傳成功"
}
}
},
/**
* @description 圖片壓縮
* @param {Object} file 壓縮的文件
* @param {Number} width 壓縮后端寬度,寬度越小,字節越小
*/
imgCompress(file,width,callBack) {
var ready = new FileReader();
ready.readAsDataURL(file);
ready.onload = () => {
this.canvasDataURL(ready.result,width,callBack);
}
},
/**
* 將以base64的圖片url數據轉換為Blob
* @param urlData
* 用url方式表示的base64圖片數據
*/
convertBase64UrlToBlob(urlData) {
var arr = urlData.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
},
/**
* @description 大於1M的圖片進行重新繪制壓縮
*/
canvasDataURL(path, obj, callback) {
var img = new Image();
img.src = path;
img.onload = () => {
// var that = this;
// 默認按比例壓縮
var w = this.width,
h = this.height,
scale = w / h;
w = obj.width || w;
h = obj.height || w / scale;
var quality = 0.7; // 默認圖片質量為0.7
//生成canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
// 創建屬性節點
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(img, 0, 0, w, h);
// 圖像質量
if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
quality = obj.quality;
}
// quality值越小,所繪制出的圖像越模糊
var base64 = canvas.toDataURL("image/jpeg", quality);
// 回調函數返回base64的值
callback(base64);
};
},
}
}
</script>
<style lang="less" scoped>
.upload-wraper {
width: 100%;
height: 100%;
}
</style>
這里是公眾號項目,所以,這里引入的第三方插件為mint-ui和vux。具體情況視情況而定。
該組件例外封裝了,后端是夠對較大圖片的處理,如果后端已經進行處理,則直接調用。如果沒有處理,則進行壓縮處理,
組件的封裝,可靈活修改,還有很多地方仍待修改
插槽,圖片上傳后,回顯可在組件內部實現。借由slot更加完美。
該組件限制了圖片文件的上傳,其他文件則不行。
引入如下:
<upload-image class="upload" :quality='.7' :url="$base.uploadUrl" :hasApi="false" @upload='uploadImage'></upload-image>
參考文獻
-
js內置file(文件)對象。https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
-
FormData對象的。https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData
-
XMLHttpRequest對象。https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
-
Image對象。https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image
-
Canvas畫布。內容較多,建議通過學習視頻了解。在本文中主要用於大型圖片的壓縮處理。
-
base64和Blob的轉換,百度很多已經封裝好的。可直接使用。eg:https://www.cnblogs.com/jiujiaoyangkang/p/9396043.html