前言:
网络上许多的文章资料,全是使用阿里云官方的SDK,ali-oss插件去做直传。可是各位素未谋面的朋友要注意,这个SDK它支持web环境使用,也就是PC端浏览器。
当项目环境切换到微信小程序,是无法使用这种方式的,当然官方也有给出微信小程序直传的文档,继续往下看。
支持配置OSS直传的callback参数,这是其它文章中没用到的
官方:如何使用ali-oss进行直传https://help.aliyun.com/document_detail/64041.html?spm=a2c4g.383954.0.0.43c25e89vo4jkS
官方:微信小程序OSS直传https://help.aliyun.com/document_detail/92883.html?spm=a2c4g.31925.0.0.24de344egXVqTI
代码:
酒过三巡我还是少说话,多贴代码。并且把一些踩坑的地方告诉大家。继续往下看。
首先需要下载三个库。
注意:官方文档是让我们使用import { Base64 } from 'js-base64';
可是它会它会报错!!!根本用不了。
所以需要换成import base64 from 'base-64';import utf8 from 'utf8'
npm install crypto-js --save
npm install base-64 --save
npm install utf8 --save
crypto-js:是阿里官方的签名库
base-64、utf8:是用来转码,配合crypto-js使用的
签名不都是后端的事情吗?没错大部分是后端。但是我在官方文档看到客户端也可以签名,然后这么随口一说。
我亲爱的后端同事便说:客户端(前端)也可以签名,那就你自己签名吧。
配置callback(可选,根据后端要求来决定是否需要)
为什么需要使用callback,这就是后端的骚操作。
他要我们把文件顺利传到OSS后,再让OSS去请求他的接口。
他接口会去做一些业务逻辑,比如改文件的重命名。而callback则是配置,后端处理业务逻辑的接口地址、接请求参数。
直传OSS配置callback是有格式要求的,并且微信小程序端、web端,callback的配置格式要求还不一样。
这里可能是因为用户们很少用到callback参数,官方文档写得也不准确。
大家按照我代码来,指定是没问题的,细节我不想说了,有问题评论区交流。
import crypto from 'crypto-js'
import base64 from 'base-64'
import utf8 from 'utf8'
<script>
methods: {
chooseAvatarEvent(event) {
const { avatarUrl } = event.detail
this.getFormDataParams().then(() => {
this.uploadFile(avatarUrl, 'tmp/', e => {})
})
},
// 计算签名。
computeSignature(accessKeySecret, canonicalString) {
console.log(crypto.enc)
return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret))
},
getPolicyBase64() {
const date = new Date()
date.setHours(date.getHours() + 1)
const policyText = {
expiration: date.toISOString(), // 设置policy过期时间。
conditions: [
// 限制上传大小。
['content-length-range', 0, 1024 * 1024 * 1024],
],
}
return policyText
},
async getFormDataParams() {
// 获取STS临时token,这是后端接口做的,找亲爱的后端
const credentials = await queryOssGetStsToken()
const policyText = this.getPolicyBase64()
const uft8Str = utf8.encode(JSON.stringify(policyText))
const policy = base64.encode(uft8Str)
const signature = this.computeSignature(credentials.AccessKeySecret, policy)
const user_id = this.userInfo.user_id
const callback = {
// 设置回调请求的服务器地址,且要求必须为公网地址。
// 后端的接口地址:https://www.baidu.com/api/xxxx
callbackUrl: credentials.callbackUrl,
// 设置回调请求消息头中Host的值,即您的服务器配置的Host值。
// host: 'yourHost',
// 设置发起回调时请求body的值。
callbackBody: 'bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}&type=avatar&user_id=' +
user_id,
// 设置发起回调请求的Content-Type。
callbackBodyType: 'application/x-www-form-urlencoded',
}
const uft8Str_callback = utf8.encode(JSON.stringify(callback))
const base64_callback = base64.encode(uft8Str_callback)
const formData = {
OSSAccessKeyId: credentials.AccessKeyId,
signature,
policy,
'success_action_status': '200',
'x-oss-security-token': credentials.SecurityToken,
callback: base64_callback,
}
this.formData = formData
console.log('formData===', this.formData)
},
uploadFile(filePath, dir, successc, failc) {
const _this = this
// 获取上传的文件类型
let fileTypeIndex = filePath.lastIndexOf('.')
let fileType = filePath.substring(fileTypeIndex)
//图片名字 可以自行定义, 这里是采用当前的时间戳 + 150内的随机数来给图片命名的
// const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + fileType
uni.uploadFile({
// 注意:阿里云OSS的访问地址,并不是接口请求域名
url: 'http://pxxxxx-xxxx-xxx.oss-cn-shenzhen.aliyuncs.com', //阿里云OSS访问地址
filePath: filePath, //要上传文件资源的路径
name: 'file', //必须填file
formData: {
'key': aliyunFileKey,
...this.formData
},
callback: this.formData.callback,
success: function(res) {
const resObj = JSON.parse(res.data)
_this.userInfo.avatar_id = resObj.data.id
_this.userInfo.avatar = resObj.data.url
console.log('上传成功===', resObj)
},
fail: function(err) {
// err.wxaddinfo = aliyunServerURL
// failc(err)
},
})
}
}
</script>
<template>
<view class="item-panel flex-1">
<u-image :src="userInfo.avatar" width="80rpx" height="80rpx" shape="circle"></u-image>
<button class="upload-box" open-type="chooseAvatar" @chooseavatar="chooseAvatarEvent"></button>
<view class="ml-20 flex ai-center">
<u-icon name="arrow-right"></u-icon>
</view>
</view>
</template>
<style lang="scss" scoped>
.item-panel {
display: flex;
position: relative;
.upload-box {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
right: 0;
top: 0;
}
}
</style>