项目需要上传图片至服务器,但因为上传带宽慢,所以需要使用阿里云对象存储OSS用于上传文件,然后将上传后的文件链接处理上传到自己项目的服务器
上传文件到服务器端规定请求头中的Content-type
使用multipart/form-data
形式,由于缺少经验自己并不清楚使用multipart/form-data
要如何发送请求
服务器端签名直传,首先是后端会提供一个接口,前端请求该接口后会返回一些字段数据,在依据返回的数据处理后再发送请求上传文件
其中callback
字段我请求的接口数据没有,查看官网服务端签名直传还是没有弄明白,该怎么将返回的数据整合在一个FromDate
对象中,然后发送post
请求将FromDate
对象传递给阿里云端的服务器
将问题二中的数据整合后,使用axios发送post
请求。由于发送请求的url
是问题二中返回数据的host
,出于浏览器的同源策略会出现跨域报错
。登陆阿里云后台配置阿里云服务器的CORS
允许跨域请求后,请求依然会出现跨域报错
使用multipart/form-data
形式上传文件,需要使用到FormData
对象
let fromData = new FormData();
/*
*append() 追加数据,当字段已经存在时,使用append()添加相同字段,相当于对一个数组使用push()
*/
fromData.append("name","oss");
/*
*set()方法,不管相同字段多次set,前面的值会被后面的值给覆盖掉
*/
fromData.set("name","oss");
fromData.set("name","lihai");
/*
*getAll() 返回一个数组,数组中包括传入字段的值
*get() 返回一个字符串类型的数据,如果某个字段多次append()数据,get方法永远都是获取第一次append的数据
*/
formData.getAll("age");// ["10", "11"]
formData.get("age");//"10"
注意点:
向FormData
对象添加任何数据类型的值都会被自动转化成字符串类型
使用axios
发送
axios应该需要二次封装,为了方便我用原生发送
import Axios from "axios";
Axios.post(url,formData,{
headers:{
"Content-Type":"multipart/form-data"
}
})
请求服务器接口会返回一些数据
获取数据后,前端部分只需要使用FromData
对象添加字段数据
上传文件使用vant
组件uploader
{{ val.title }}
Vue
核心代码
onChange: async function (e) {
let data = await this.getOss();//服务接口返回数据
const files = e,
file = files.file,
maxLen = 9;
if (!/^image\/.+$/.test(file.type)) {
Toast("请选择图片");
return;
}
/**
* 服务器签名直连
*/
let fileName = `${Date.now()}.${file.type.split("/")[1]}`,//使用时间来定义文件名
pathName = `${data.dir}${fileName}`;//相对路径
this.Files.set("key", pathName);//key 唯一值 即相对路径
this.Files.set("policy", data.policy);//服务器返回的policy
this.Files.set("OSSAccessKeyId", data.accessId);//服务器返回的accessId
this.Files.set("success_action_status", "200");//定义成功为200
this.Files.set("signature", data.signature);//服务器返回的signature
this.Files.set("name", fileName);//文件名
this.Files.set("file", file, fileName);//文件对象
this.fileUpdata(data.host, pathName);
},
fileUpdata: async function (url, fileName) {
let res = await _http.post(url, this.Files, {//发送请求的url就是服务器返回的host
"Content-Type": "multipart/form-data",
});
},
终于将请求发出去了,但没想到它报跨域错误。还是在阿里云后台已经把CORS
修改成*
的前提下。
参考文档:https://help.aliyun.com/document_detail/31870.html?spm=a2c4g.11186623.2.9.1aba5d49k0Z2dG
xhr.withCredentials=false;
表示跨域请求时,不发送cookit资源
我想到Axios
二次封装,我把这个字段设置成了true
,
axios.defaults.withCredentials = true;
将axios.defaults.withCredentials
设置回false
后,成功了!!!!
https://ywja-public-bucket.oss-cn-hangzhou.aliyuncs.com/
加上前面自己拼接上传的文件的相对路径就可以在浏览器上访问该图片了
本来没有东西写了,但我又遇到了个问题。在移动端上传图片文件大小会很大,占流量,而且上传后,再加载浏览会加载很慢,所有在上传图片文件时,要将图片进行压缩一次
前端压缩使用canvas
进行操作,在原有代码上修改
onChange: async function (e) {
let data = await this.getOss();
const files = e,
file = files.file,
maxLen = 9;
if (!/^image\/.+$/.test(file.type)) {
Toast("请选择图片");
return;
}
if (this.htmlContext.length === maxLen) {
return Toast("图片已超出数量");
}
this.showIconFn("上传中", "loading");
/**
* 服务器签名直连
*/
let fileName = `${Date.now()}.${file.type.split("/")[1]}`,
pathName = `${data.dir}${fileName}`;
this.Files.set("key", pathName);
this.Files.set("policy", data.policy);
this.Files.set("OSSAccessKeyId", data.accessId);
this.Files.set("success_action_status", "200");
this.Files.set("signature", data.signature);
this.Files.set("name", fileName);
//图片压缩
this.compression(file, data.host, pathName);
},
compression(file, host, pathName) {
let image = document.createElement("img"),
max = 600;//图片宽高
self = this;
//图片加载后触发函数
image.onload = function () {
let w = image.width,
h = image.height,
nw = w,
nh = h;
if (w > max) {
nw = max;
nh = h / (w / nw);
}
if (h > max) {
nh = max;
nw = w / (h / nh);
}
const canvas = document.createElement("canvas"); //新建一个画布,用来处理图像
let ctx = canvas.getContext("2d") || canvas.getContext("2D"); //画布为2d
canvas.width = nw;
canvas.height = nh; //初始化画布的大小为之前的计算结果
ctx.clearRect(0, 0, canvas.width, canvas.height); //清空画布
ctx.drawImage(image, 0, 0, nw, nh); //绘制画布
/*
* 图片压缩关键在于canvas方法toBlob,第一个参数是回调函数,第二个参数为转化的图片格式,第三个参数是像素位数。将图片转化成image/jpg或image/webp大小可以变小
*/
canvas.toBlob(
(blob) => {
//oss上传Blob文件会报错,所以再将Blob文件转化回File形式在上传阿里oss
let files = new File([blob], file.name); //拿到的blod文件转化成文件
self.Files.set("file", files, file.name); //将压缩后的图片二进制文件传入表单对象
self.fileUpdata(host, pathName);
},
"image/webp",
0.95
);
};
image.src = URL.createObjectURL(file);
},