乐趣区

关于后端:Thinkphp6Uniapp微信小程序上传图片到阿里云OSS

阿里云对象存储 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 胜利

退出移动版