阿里云对象存储 OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务。本实例微信小程序直传文件参考官方文档:https://help.aliyun.com/zh/oss/use-cases/use-wechat-mini-prog...,使用STS临时访问凭证访问OSS官方文档:https://www.alibabacloud.com/help/zh/oss/developer-reference/...,另外使用STS进行临时授权之PHP授权访问参考官方文档:https://help.aliyun.com/zh/oss/developer-reference/authorize-...。使用STS授权用户直接访问OSS的流程如下:
开通阿里云OSS开通阿里云oss试用版:https://free.aliyun.com/?pipCode=oss&spm=5176.7933691.J_52537...
创建存储空间Bucket存储空间(Bucket)是用于存储对象(Object)的容器
创建Bucket存储目录文件列表新建test目录作为图片存放位置
获取Bucket名称和Bucket域名点击浏览进入Bucket基本信息,保存存储空间名称、Endpoint地域节点、Bucket域名
创建RAM用户与角色云账号 AccessKey 是您访问阿里云 API 的密钥,具有账户的完全权限,使用 RAM 用户(而不是云账号)的 AccessKey 进行 API 调用
获取AccessKey ID和AccessKey Secret
为RAM用户添加权限:AliyunOSSFullAccess、AliyunSTSAssumeRoleAccess,在左侧搜索框输入AliyunOSSFullAccess与AliyunSTSAssumeRoleAccess进行添加,然后点确定进行保存。
创建角色:第一步选择阿里云账号
第二步配置角色
创建完成之后点击“为角色授权”
还是添加AliyunOSSFullAccess、AliyunSTSAssumeRoleAccess权限,在左侧搜索框输入AliyunOSSFullAccess与AliyunSTSAssumeRoleAccess进行添加,然后点确定进行保存。
获取角色ARN
为角色授予上传文件的权限在创建权限策略页面,单击脚本编辑,然后在策略文档输入框中赋予角色向目标存储空间examplebucket下的目录上传文件的权限。具体配置示例如下。
创建Bucket授权策略
配置Bucket跨域访问客户端进行表单直传到OSS时,会从浏览器向OSS发送带有Origin头的请求消息。OSS对带有Origin头的请求消息会进行跨域规则(CORS)的验证。因此需要为Bucket设置跨域规则以支持Post方法。
微信小程序配置域名白名单为微信小程序配置域名白名单,以实现微信小程序和OSS Bucket之间的正常通信。登录微信小程序平台,将上传和下载的合法域名填写为Bucket的外网访问域名。
服务端生成签名使用服务端签名时,需要先搭建一个签名服务,然后由客户端调用签名服务生成签名。本例使用Thinkphp6生成签名。首先安装thinkphp6:
C:\phpstudy_pro\WWW> composer create-project topthink/think tp
使用composer require alibabacloud/sts-20150401命令安装STS依赖,使用composer require alibabacloud/sdk命令安装PHP SDK依赖。
C:\phpstudy_pro\WWW>tp> composer require alibabacloud/sts-20150401
C:\phpstudy_pro\WWW>tp> composer require alibabacloud/sdk
创建生成签名控制器api.php、配置文件upload.php
upload.php文件配置阿里云oss参数
<?php
return [
'storage' => '0',
'oss_ak' => '',//阿里云AccessKeyId
'oss_sk' => '',//阿里云AccessKeySecret'
'oss_host' => '',//阿里云Bucket 域名
'oss_endpoint' => 'oss-cn-hangzhou.aliyuncs.com',//阿里云 Endpoint(地域节点)
'oss_bucket' => '',//bucket名称
'oss_role_arn' => '', // 角色访问控制RoleArn
'oss_role_session_name' => 'stahangdeng', // 临时凭证名称,随意
];
后端代码实现
<?php
namespace app\controller;
use think\Request;
use think\facade\Config;
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use app\BaseController;
class Api extends BaseController
{
private $ak='';
private $sk='';
private $host='';
private $bucket='';
private $endpoint='';
private $roleArn='';
private $roleSessionName='';
public function __construct(Request $request)
{
//配置阿里云参数
empty($this->ak) && $this->ak = Config::get('upload.oss_ak');
empty($this->sk) && $this->sk = Config::get('upload.oss_sk');
empty($this->host) && $this->host = Config::get('upload.oss_host');
empty($this->bucket) && $this->bucket = Config::get('upload.oss_bucket');
empty($this->endpoint) && $this->endpoint = Config::get('upload.oss_endpoint');
empty($this->roleArn) && $this->roleArn = Config::get('upload.oss_role_arn');
empty($this->roleSessionName) && $this->roleSessionName = Config::get('upload.oss_role_session_name');
}
/**
* 阿里云Sts凭证
*/
public function getStsToken()
{
AlibabaCloud::accessKeyClient($this->ak, $this->sk)
->regionId('cn-hangzhou')
->asDefaultClient();
try {
$result = AlibabaCloud::rpc()
->product('Sts')
->scheme('https') // https | http
->version('2015-04-01')
->action('AssumeRole')
->method('POST')
->host('sts.aliyuncs.com')
->options([
'query' => [
'RegionId' => "cn-hangzhou",
'RoleArn' => $this->roleArn,
'RoleSessionName' => $this->roleSessionName,
],
])
->request();
$resultObj = $result->toArray();
$credentials = $resultObj['Credentials'];
$credentials['host'] = $this->host;
return json($credentials);
} catch (ClientException $e) {
return error($e->getErrorMessage());
} catch (ServerException $e) {
return error($e->getErrorMessage());
}
}
uniapp搭建小程序创建项目
把项目运行到微信开发者工具
运行到小程序模拟器
安装crypto-js和base64-js执行以下命令安装js
C:\phpstudy_pro\WWW\wx-uniapp> npm install crypto-js
C:\phpstudy_pro\WWW\wx-uniapp> npm install js-base64
引入js
import crypto from 'crypto-js';
import { Base64 } from 'js-base64';
前端代码实现
在wx-uniapp\pages\index\index.vue文件添加以下代码
<template>
<view class="content">
<view class="cell-left">
<view class="cell-text">照片上传:</view>
<view class="cell-text" style="color: #8C8C8C;width: 180px;">(最多上传3张图片)</view>
</view>
<view class="cell-left">
<view class="qtpicker">
<!-- 选中待上传的图片 -->
<view class="preImgs" v-for="(val,index) in preImgUrl" :key='index'>
<image style="border-radius: 6px;" mode="" :src="val" @click="showImg(val,index)">
</image>
<!-- 删除某张图片 -->
<view v-show="isShowDelImgBtn">
<image class="cuo" mode="" src="/static/delete-icon.png" @click="delImg(index)">
</image>
</view>
</view>
<view v-show="isShowAddImgBtn">
<view class="img-item upload-icon" @click="chooseImg"></view>
</view>
</view>
</view>
</view>
</template>
<script>
import crypto from 'crypto-js';
import { Base64 } from 'js-base64';
export default {
data() {
return {
//title: 'Hello',
preImgUrl: [], //本地预览的图片数据
ossAccessKeyId:'',
ossAccessKeySecret:'',
host:'',
securityToken:'',
policy:'',
isShowDelImgBtn:true,
isShowAddImgBtn: true,
}
},
onLoad() {
},
created() {
this.getStsToken();
},
methods: {
// 将方法标记为异步
async getStsToken() {
let that = this
// 调用后端接口
const result = uni.request({
url: 'http://localhost/api/getStsToken',
method: 'POST',
}).then(response => {
// response 就是 Promise 的 [[PromiseResult]] 属性对应的值
const { data } = response;
console.log(data);
// 现在你可以直接访问和过滤data中的内容
this.host = data.host;
that.ossAccessKeyId = data.AccessKeyId;
that.ossAccessKeySecret = data.AccessKeySecret;
that.securityToken = data.SecurityToken;
const policyText = {
expiration: data.Expiration, // policy过期时间。根据自己接口返回的格式进行获取数据
conditions: [
// 限制上传大小。
["content-length-range", 0, 1024 * 1024 * 1024],
],
};
this.policy = Base64.encode(JSON.stringify(policyText)) // policy必须为base64的string。
}).catch(error => {
console.error('获取STS Token时出错:', error);
});
},
computeSignature(accessKeySecret, canonicalString) {
return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret));
},
// 选择图片
chooseImg() {
let that = this
uni.chooseImage({
// count: 允许上传的照片数量
count: 3, //h5无法限制
// sizeType: original 原图,compressed 压缩图,默认二者都有
sizeType: "original,compressed",
success: function(res) { //选择成功,将图片存入本地临时路径,可删除,可查看,等待上传
console.log(res, '选择成功')
// 如果限制图片大小,则添加判断
res.tempFiles.map(val => {
// 判断本次上传限制的图片大小
if (val.size > 10485760) {
uni.showToast({
icon: 'none',
title: '上传的图片大小不超过10M'
})
return
}
// 判断本次最多上传多少照片
that.imgNum++
if(that.imgNum==3){
that.isShowAddImgBtn=false
uni.showToast({
icon: 'none',
title: '最多上传3张图片'
})
}
if (that.imgNum > 3) {
that.imgNum = 3
uni.showToast({
icon: 'none',
title: '上传的图片最多不能超过3张'
})
return
}
const filePath = val.path; // 待上传文件的文件路径。
//把临时路径添加进数组,渲染到页面
that.preImgUrl.push(filePath)
// 加入时间戳-以免文件名重复
let unixTime = String(Date.parse(new Date()) / 1000)
//获取最后一个.的位置
let fileIndex = filePath.lastIndexOf(".");
//获取文件后缀
let fileExt = filePath.substring(fileIndex + 1);
//文件名
let key = 'test/'+unixTime+'.'+fileExt;
const signature = that.computeSignature(that.ossAccessKeySecret, that.policy);
console.log(signature);
//上传图片到阿里云oss
const host = that.host;
const policy = that.policy;
const ossAccessKeyId = that.ossAccessKeyId;
const securityToken = that.securityToken;
uni.uploadFile({
url: host,
filePath: filePath,
name: 'file', // 必须填file。
formData: {
key,
policy,
OSSAccessKeyId: ossAccessKeyId,
signature,
'x-oss-security-token': securityToken,
//success_action_status: 200, // 自定义成功返回的http状态码,默认为204
},
success: (res) => {
console.log(res);
if (res.statusCode === 204) {
//console.log('上传成功');
}
},
fail: err => {
console.log(err);
}
});
})
}
})
},
//点击小图查看大图片
showImg(val, index) {
console.log(val, '点击了')
let that = this
uni.previewImage({
// 对选中的图片进行预览
urls: that.preImgUrl, //图片数组 // urls:['',''] 图片的地址 是数组形式
current: index, //当前图片的下标
})
},
//删除某张图片,从本地的临时路径图片中, 删除路径即可
delImg(index) {
this.imgNum--;
this.preImgUrl.splice(index, 1)
if(this.imgNum<3){
this.isShowAddImgBtn=true;
}
},
}
}
</script>
<style lang="scss" scoped>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
.upload-icon {
box-sizing: border-box;
border: 2rpx solid #bfbfbf;
}
.upload-icon:before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 60rpx;
height: 6rpx;
background-color: #bfbfbf;
margin: -3rpx 0 0 -30rpx;
border-radius: 5rpx;
}
.upload-icon::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 6rpx;
height: 60rpx;
background-color: #bfbfbf;
margin: -30rpx 0 0 -3rpx;
border-radius: 5rpx;
}
.cell-left {
display: flex;
align-items: center;
padding: 8px;
.cell-icon {
width: 50rpx;
height: 50rpx;
}
.cell-text {
color: #595959 ;
font-size: 15px;
margin-left: 20rpx;
//width: 180rpx;
}
}
.img-item {
width: 150rpx;
height: 150rpx;
position: relative;
box-sizing: border-box;
margin: 15rpx;
.img {
width: 100%;
height: 100%;
}
.img-delete-box {
width: 40rpx;
height: 40rpx;
position: absolute;
right: 0;
top: 0;
.img-delete-icon {
width: 100%;
height: 100%;
}
}
}
.qtpicker {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 auto;
padding: 10rpx 0;
.preImgs {
margin: 13rpx;
position: relative;
image {
width: 200rpx;
height: 200rpx;
}
.cuo {
width: 17pt;
height: 17pt;
//line-height: 12px;
//text-align: center;
///* font-size: 16px; */
//border-radius: 50%;
//background-color: #223E4B;
//color: #FFFFFF;
position: absolute;
right: 0px;
top: 0px;
}
}
}
</style>
上传图片前端微信小程序开发工具点击图片上传,图片上传成功返回code204
Bucket文件列表显示上传图片成功
点击图片详情,图片正常显示
到此微信小程序上传图片到阿里云oss成功
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。