最近项目开发商品库模块,有一个商品图片上传的功能,需要用到阿里云的oss直传服务器技术,最终实现的过程比较坎坷,记录下来以便后期使用。
本次项目用到的UI框架是element UI,直传过程是先请求后台,后台去进行签名,然后返回签名信息给我,我再拿着签名信息和其他图片数据进行直传,由于阿里云的文档中只有js的demo,应用到vue中还需要进一步的封装,所以索性通过查阅相关数据以及自己摸索,通过element UI的上传组件实现了oss直传,代码如下所示:
<el-upload class="oss-upload" ref="upload1"
:auto-upload="false" :show-file-list="false"
accept=".jpg,.jpeg,.png,.JPG,.JPEG,.PNG"
action:on-change="imgPreview" list-type="picture">
<el-button size="small" type="primary">选择列表图</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过2M</div>
</el-upload>
//判断图片格式和大小
imgPreview(file) {
if (file.status === "ready") {
const isJPG = file.raw.type === 'image/jpeg' || file.raw.type === 'image/png'
const isLt5M = file.raw.size / 1024 / 1024 < 2
if (!isJPG) {
this.$message.error('上传图片只能是 PNG、JPG 格式!')
return false
} else if (!isLt5M) {
this.$message.error('上传图片大小不能超过2MB!')
return false
} else {
//合适的图片会存入带上传图片数组
this.fileList.push(file)
}
}
},
//获取上传图片的名字
get_suffix(filename) {
let pos = filename.lastIndexOf('.')
let suffix = ''
if (pos != -1) {
suffix = filename.substring(pos)
}
return suffix
},
//生成32位随机字符附在名字前面
random_string(len) {
len = len || 32
let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
let maxPos = chars.length
let pwd = ''
for (let i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos))
}
return pwd
},
//图片上传拿签名
picUpload(file,type) {
let fileObj = file
let data = this.upParams;//提前请求拿到的签名信息
let ossData = new FormData()//创建oss数据对象
ossData.append('name', fileObj.name)
let filename = fileObj.name
let picName = this.random_string(32) + this.get_suffix(filename)
let keyValue = data.dir + picName
if(type == 'list'){
this.upList_picture.push(picName)
}else if(type == 'detail'){
this.upDetail_picture.push(picName)
}
ossData.append('key', keyValue)
ossData.append('policy', data.policy)
ossData.append('OSSAccessKeyId', data.accessid)
ossData.append('success_action_status', 200)
ossData.append('signature', data.signature)
ossData.append('file', fileObj, fileObj.name)
//实际项目中是将图片存在待上传列表中,点击确定后先进行上传,然后再把上传的图片信息发给后台保存,
//这里的问题就是如果图片还没有上传成功就把数据发给后台的话,就会导致图片访问不到。
//所以这里是通过返回一个promise,后面通过promise.all方法来判断是否全部上传成功。具体代码再下一个片段中
return new Promise((resolve, reject) => {
this.$http.post(data.host, ossData, {
headers: {
'Content-Type': 'multipart/form-data'
},
}).then(res => {
resolve(res);
}).catch(error => {
reject(error);
})
});
}
判断图片是否上传成功代码:
let promistArr = []
//fileList是待上传列表的数组,里面有图片文件的相关数据,picUpload是前面的上传函数
this.fileList.forEach(list1 => {
if (list1.raw) {
//将函数return的结果push进数组
promistArr.push(this.picUpload(list1.raw,'list'))
}
})
//判断数组中所有promis的结果并进行相关处理
Promise.all(promistArr).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
this.$message({
type: 'warning',
message: "图片上传失败"
});
})
走到这里可以正常情况下是可以成功了,但我遇到了一个问题,就是每次上传之后总会报跨域错误,查看network发现图片已经上传成功了,但报错就无法获取结果,也就无法进行后面的操作了
后来经过一番查阅,找到了这篇文章,情况相似,于是尝试性的对请求进行了一些改动,果然成功了
CORS: credentials mode is 'include'
return new Promise((resolve, reject) => {
this.$http.post(data.host, ossData, {
headers: {
'Content-Type': 'multipart/form-data'
},
withCredentials: false //这里修改了withCredentials为false
}).then(res => {
resolve(res);
}).catch(error => {
reject(error);
})
});
因为项目涉及到登录状态问题,每次请求需要带着cookie去判断,超时后就要重新登录,结果导致了图片上传的问题,不过幸好经过查阅之后解决了这个问题,记录下来方便自己学习的同时也希望能帮到其他被困扰的朋友。