昨天在写博客的我的动态页面,里面涉及到了图片上传。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);
});复制代码