昨天在寫博客的我的動態頁面,裏面涉及到了圖片上傳。vue
以前我都是用的別人的插件和elementUI的upload組件。可是如今無法用了。jquery
頁面的效果差異有點大,若是改elementUI的樣式,會很累。ios
這時候很愁人啊。(懶啊!)ajax
這是頁面開發的效果,很像qq空間的感受。element-ui
我參考了(簡稱xhr)和Fetchjson
文檔地址:axios
XMLHttpRequest:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest後端
fetch:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_APIapi
上面的xhr其實就是咱們用的最多的http的核心函數,早年的jquery.ajax和現在的axios都是用的這個。數組
fetch,兼容性差點,可是是最新的標準函數。已經沒有那麼多亂七八糟的東西了。把xhr幹成了ajax的樣子。
總結:
xhr的狀況下須要完整的本身寫一遍ajax函數
fetch呢是新標準,缺點是ie全系不支持,而且edge也有部分不支持。
哎呀:就是懶啊。不想本身寫。(並且:本身寫封裝要考慮好多東西)
這個嗎,沒什麼多說的。確定不會選jq。
並且我早先的時候本身封裝過axios,造成了本身項目下的專屬使用的ajax函數。因此此次的封裝是在本身封裝的ajax函數上進行復用。
這裏放出下我現現在的總體的axios內部的狀況。
主角是下半部分的axios.upload
"use strict";
import Vue from "vue";
import axios from "axios";
import VueAxios from "vue-axios";
import { Notification, Loading } from "element-ui";
import apiList from "./api-list"; //接口列表數據
const store = require("store");
import control from "@/common/control_center/index";
// axios全局導入設置
Vue.use(VueAxios, axios);
Vue.prototype.$api = apiList; //將接口列表數據綁定到vue全局
//自定義消息提示函數信息
let customMsg = {
//成功信息提示
sucIfno(info) {
Notification({
title: "答對了!",
type: "success",
message: info
});
},
//錯誤信息提示
errIfno(info) {
Notification({
title: "答錯了呢",
type: "error",
message: info
});
}
};
// 受權函數封裝
const authorize = herders => {
const user_info = store.get("user_info");
if (user_info && user_info.sign) {
herders.Authorization = user_info.sign;
return herders;
} else {
return herders;
}
};
// axios函數封裝
const ajax = ({
url = "",
loading = false, //加載攔截
baseURL = apiList.baseURL,
data = {},
headers = { "Content-Type": "application/json;charset=UTF-8" }, //頭部信息處理
method = "get",
success = false, //成功信息提示
error = true, //錯誤信息提示
timeout = 1000
}) => {
// 數據過濾,過濾字段中空數據等
const filter = record => {
for (let key in record) {
!record[key] && delete record[key];
}
return record;
};
//接口全局加載提示
let loadingInstance = "";
if (loading !== false) {
loadingInstance = Loading.service({
lock: true,
text: loading !== true ? loading : "努力加載中……",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.5)"
});
}
return new Promise((suc, err) => {
// 預處理數據部分
method = method.toLocaleLowerCase(); //轉化爲小寫
headers = authorize(headers);
axios({
url: url,
baseURL: baseURL,
headers: headers,
method: method,
[method === "post" ? "data" : "params"]: filter(data),
timeout: timeout
})
.then(response => {
loadingInstance && loadingInstance.close();
// 刷新口令以及接口判斷是否退出登陸
if (!control.refresh_sign_or_out(response)) {
customMsg.errIfno("數據異常,退出登陸");
err(response);
}
const res = response.data;
//自定義成功失敗處理,code值表明後端接口數據處理成功或者失敗
// 後端返回格式
/*data = {
code: 0, // 0成功,1失敗
msg: "", // 錯誤信息
data: "" // 數據
};*/
if (res && res.code === 0) {
success !== false &&
customMsg.sucIfno(success === true ? "信息處理成功" : success);
suc(res);
} else {
error !== false &&
customMsg.errIfno(res.msg ? res.msg : "信息處理失敗");
err(res);
}
})
.catch(e => {
console.log(e);
loadingInstance && loadingInstance.close();
error !== false ? customMsg.errIfno("接口異常") : false;
//catch表明網絡異常部分和後端返回結果無關
err(e);
});
});
};
//暴露的ajax函數,進一步封裝節流和防抖
let shakeTime = "";
axios.ajax = options => {
//參數預處理
let shake = options.shake || false; //不等於false直接傳true或者防抖時間
//防抖函數處理
if (shake === false) {
//不進行防抖處理
return new Promise((suc, err) => {
ajax(options)
.then(e => {
suc(e);
})
.catch(e => {
err(e);
});
});
} else {
//進行防抖處理
return new Promise((suc, err) => {
shakeTime && clearTimeout(shakeTime);
let callNow = !shakeTime;
if (callNow) {
ajax(options)
.then(e => {
suc(e);
})
.catch(e => {
err(e);
});
}
shakeTime = setTimeout(
() => {
shakeTime = null;
}, //見註解
shake === true ? 700 : shake
);
});
}
};
axios.upload = options => {
// 對uri地址進行數據拼接
const new_url = obj => {
if (obj) {
let fields = "";
for (let key in obj) {
fields = fields + `${key}=${obj[key]}`;
}
return "?" + fields;
} else {
return "";
}
};
options.fdata = options.fdata || ""; //文件上傳的url拼接地址
options.success = options.success || "文件上傳成功";
options.url = options.url + new_url(options.fdata);
options.headers = options.headers || {};
let header = { "Content-Type": "multipart/form-data" };
for (let i in header) {
options.headers[i] = header[i];
}
options.method = "post";
options.multiple = options.multiple || false; //是否多文件,默認false
//文件類型驗證,注意傳入數組,默認["image/jpeg", "image/png"]
options.type = options.type || ["image/jpeg", "image/png"];
options.size = options.size || 0; //文件大小限制,默認0
options.max = options.max || 5; //最多上傳幾個文件
//文件驗證處理
let input = document.createElement("input");
input.type = "file";
options.multiple ? (input.multiple = "multiple") : "";
input.click();
return new Promise((suc, err) => {
let type = options.type;
input.addEventListener("input", watchUpload, false);
function watchUpload(event) {
//移除監聽
let remove = () => {
input.removeEventListener("input", watchUpload, false);
input = null;
};
const file = event.path[0].files;
const len = file.length;
// 文件數量限制
if (len > options.max) {
err(file);
remove();
customMsg.errIfno("文件個數超過" + options.max);
return false;
}
let formData = new FormData();
for (let i = 0; i < len; i++) {
// 文件大小限制
if (options.size !== 0 && file[i].size / 1024 / 1024 > options.size) {
err(file[i]);
remove();
customMsg.errIfno(file[i].name + "文件超過指定大小");
return false;
}
// 文件類型限制
if (type.length > 0 && !type.includes(file[i].type)) {
err(file);
remove();
customMsg.errIfno(file[i].name + "文件類型爲" + file[i].type);
return false;
}
formData.append("dhtUpload", file[i], file[i].name);
}
options.data = formData;
// 最終進行文件上傳
options.baseURL = "";
ajax(options)
.then(e => {
suc(e);
})
.catch(e => {
err(e);
});
}
});
};
export default axios;複製代碼
注意,其實自己是直接用的axios效果沒什麼差異。不要被我本身封裝的ajax迷糊了。
這裏的初衷就是我以函數的形式,而不在頁面上存在input元素。我看網上不少教程都是預先定義好input節點的
這裏我放出本身一個基於input的顏色選擇器。文件的方式相似這個
//顏色選擇器
colorSelect() {
let input = document.createElement("input");
input.type = "color";
input.click();
input.addEventListener("input", watchColorPicker, false);
function watchColorPicker(event) {
//console.log(event.target.value);
//移除監聽
input.removeEventListener("input", watchColorPicker, false);
input = "";
}
}複製代碼
咱們要的是path裏面的值。看看path裏面
注意這裏就一個input元素,咱們取input中的files數組就是咱們的文件了。
這裏看看單個文件的狀況
這裏咱們能看到每一個文件的大小,類型的狀況。這些在後面會使用到。好比咱們限制文件上傳個數,文件的大小和文件類型
這裏我直接放MDN文檔了。
https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects
其實咱們只須要知道,是下面這樣用的就好了。
let formData = new FormData();
formData.append("dhtUpload", file[i], file[i].name);複製代碼
append參數:文件名稱,文件,文件名稱
這裏其實我沒太理解
放一下elementUI源碼中的一個狀況。
formData.append(option.filename, option.file, option.file.name);複製代碼
具體沒太懂。
const ajax = ({
url = "",
loading = false, //加載攔截
baseURL = apiList.baseURL,
data = {},
headers = { "Content-Type": "application/json;charset=UTF-8" }, //頭部信息處理
method = "get",
success = false, //成功信息提示
error = true, //錯誤信息提示
timeout = 1000
})複製代碼
上面就是個人ajax函數的參數。
// 對uri地址進行數據拼接
const new_url = obj => {
if (obj) {
let fields = "";
for (let key in obj) {
fields = fields + `${key}=${obj[key]}`;
}
return "?" + fields;
} else {
return "";
}
};
options.baseURL = ""; //我的處理,須要兼容以前的elementui等插件的上傳
options.fdata = options.fdata || ""; //文件上傳的url拼接地址
options.success = options.success || "文件上傳成功";
options.url = options.url + new_url(options.fdata);
options.loading = options.loading || true;
options.headers = options.headers || {};
options.headers["Content-Type"] = "multipart/form-data";
options.method = "post";
options.multiple = options.multiple || false; //是否多文件,默認false
//文件類型驗證,注意傳入數組,默認["image/jpeg", "image/png"]
options.type = options.type || ["image/jpeg", "image/png"];
options.size = options.size || 0; //文件大小限制,默認0
options.max = options.max || 5; //最多上傳幾個文件
複製代碼
看着參數好可能是吧。其實沒多少。
解析一下:
fdata:由於文件上傳地址確定是多樣的。可是咱們又須要傳遞一些文件以外的信息。這時候其實能夠在地址欄拼接參數。後端也是能獲取到的。
new_url 和fdata是爲了這個存在的。
success:單純的的文件上傳提示信息。這個是由於以前ajax封裝將提示信息封裝進去了,可是默認成功不提示。
headers:這個沒什麼多說的,可是爲了保證headers可能有別的存在,那麼就這樣進行設置了。"Content-Type":"multipart/form-data"這個是必須的
loading:文件加載的時候全屏出現加載提示
method:必須是post
multiple:這個這裏缺失了一點,就是爲了控制文件是多文件仍是單文件上傳
type:文件類型的控制。這裏傳入數組。默認控制圖片文件。傳入什麼默認限制這些類型
size:限制每一個文件的大小,0不限制
max:限制文件個數,單文件沒意義
//文件驗證處理
let input = document.createElement("input");
input.type = "file";
options.multiple ? (input.multiple = "multiple") : "";
input.click();複製代碼
代碼就這麼簡單,這裏options.multiple就是剛纔參數設置的意義所在了
這裏我放整個控制部分,而後拆開解析
return new Promise((suc, err) => {
let type = options.type;
input.addEventListener("input", watchUpload, false);
function watchUpload(event) {
//console.log(event);
//移除監聽
let remove = () => {
input.removeEventListener("input", watchUpload, false);
input = null;
};
const file = event.path[0].files;
const len = file.length;
// 文件數量限制
if (len > options.max) {
remove();
customMsg.errIfno("文件個數超過" + options.max);
err(file);
return false;
}
let formData = new FormData();
for (let i = 0; i < len; i++) {
// 文件大小限制
if (options.size !== 0 && file[i].size / 1024 / 1024 > options.size) {
remove();
customMsg.errIfno(file[i].name + "文件超過指定大小");
err(file[i]);
return false;
}
// 文件類型限制
if (type.length > 0 && !type.includes(file[i].type)) {
remove();
customMsg.errIfno(file[i].name + "文件類型爲" + file[i].type);
err(file);
return false;
}
formData.append("dhtUpload", file[i], file[i].name);
}
options.data = formData;
// 最終進行文件上傳
ajax(options)
.then(e => {
suc(e);
})
.catch(e => {
err(e);
});
// 沒有問題下,清空監聽。
remove();
}
});複製代碼
//console.log(event);
//移除監聽
let remove = () => {
input.removeEventListener("input", watchUpload, false);
input = null;
};
const file = event.path[0].files;
const len = file.length;
// 文件數量限制
if (len > options.max) {
remove();
customMsg.errIfno("文件個數超過" + options.max);
err(file);
return false;
}
let formData = new FormData();複製代碼
這裏看註釋也知道我幹了什麼。
第一:我獲取文件數組。而且預先將移除監聽處理好,由於不少地方會用到。
第二:我判斷文件數量限制。超過的話,promise(代碼中的err(file))返回錯誤;而且註銷監聽
第三:先聲明formdata對象
for (let i = 0; i < len; i++) {
// 文件大小限制
if (options.size !== 0 && file[i].size / 1024 / 1024 > options.size) {
remove();
customMsg.errIfno(file[i].name + "文件超過指定大小");
err(file[i]);
return false;
}
// 文件類型限制
if (type.length > 0 && !type.includes(file[i].type)) {
remove();
customMsg.errIfno(file[i].name + "文件類型爲" + file[i].type);
err(file);
return false;
}
formData.append("dhtUpload", file[i], file[i].name);
}
options.data = formData;複製代碼
這裏應該很明顯了。我遍歷每一個file文件,而且進行文件大小和文件類型的判斷限制。
最後將經過的formdata數據賦值給ajax的data對象中
// 最終進行文件上傳
ajax(options)
.then(e => {
suc(e);
})
.catch(e => {
err(e);
});
// 沒有問題下,清空監聽。
remove();複製代碼
this.axios
.upload({
url: this.$api.static().upload_pictures
})
.then(e => {
console.log("成功", e);
})
.catch(e => {
console.log("錯誤", e);
});複製代碼