uniapp+python使用临时签名上传腾讯云oss对象储存方案

概述

uniapp使用临时签名上传腾讯云oss对象储存方案,支持小程序、app、h5;
前端不依赖腾讯云SDK工具类;
后端使用python实现,需要安装qcloud-python-sts;
其中计算文件md5值使用了条件编译,因为每个环境获取ArrayBuffer方案不一样都不兼容;
 pip install qcloud-python-sts==3.1.6

那些踩过的坑🕳

  • 官方方案小程序SDK,但是小程序SDK在APP环境由于无法获取file://类型地址的文件;
  • 官方方案JS-SDK,此方案由于要使用file对象然而APP端无法使用Blob工具类;

uniapp实现

import SparkMD5 from "spark-md5"; //md5工具类 使用npm安装
import {api_getBucketAndRegionSelf
} from "@/api/common";//此处是后端拿临时密钥等信息的
import {OSS_BASE_URL
} from '../config';//此处获取到的是自定义的域名/*** 上传文件 路径为 年/月/日/keypath/fileMD5.xx* @param {string} keyPath 文件分路径(可留空) 例:users/user1* @param {string} file 文件路径* @returns {Promise<string|null>} 文件上传后的URL*/
async function putObjectAutoPath(keyPath, file) {console.log("getMD5FileName")try {console.log("getMD5FileName")const md5FileName = await getMD5FileName(file);const datePath = getDatePath();const uploadPath = `${datePath}${keyPath.trim() ? `${keyPath.trim()}/` : ''}${md5FileName}`;console.log("上传路径为:" + uploadPath);console.log("图片路径=>" + file);const res = await api_getBucketAndRegionSelf(uploadPath);console.log(res)const formData = {key: res.data.cosKey,policy: res.data.policy, // 这个传 policy 的 base64 字符串success_action_status: 200,'q-sign-algorithm': res.data.qSignAlgorithm,'q-ak': res.data.qAk,'q-key-time': res.data.qKeyTime,'q-signature': res.data.qSignature,'x-cos-security-token': res.data.securityToken};const uploadResult = await uploadFile('https://' + res.data.cosHost, file, formData);console.log('上传成功:', uploadResult);return OSS_BASE_URL + res.data.cosKey;} catch (error) {console.error('上传失败:', error);throw error;}
}/*** 生成文件夹路径 [时间命名]* @returns {string} keyPath*/
function getDatePath() {const date = new Date();const year = date.getFullYear();const month = String(date.getMonth() + 1).padStart(2, "0");const day = String(date.getDate()).padStart(2, "0");return `/${year}/${month}/${day}/`;
}/*** 计算文件的 MD5 哈希值* @param {File|string} file 文件对象或文件路径* @returns {Promise<string>} MD5 哈希值*/
function calculateMD5(file) {return new Promise((resolve, reject) => {// 在 Web 环境下使用 FileReader//#ifdef H5console.log("执行md5值计算H5", file);const xhr = new XMLHttpRequest();xhr.open('GET', file, true);xhr.responseType = 'blob';xhr.onload = function() {if (xhr.status === 200) {const blob = xhr.response;const reader = new FileReader();reader.onload = (e) => {const binary = e.target.result;const spark = new SparkMD5.ArrayBuffer();spark.append(binary);resolve(spark.end());};reader.onerror = reject;reader.readAsArrayBuffer(blob);} else {reject(new Error('Failed to fetch blob'));}};xhr.onerror = reject;xhr.send();//#endif//#ifndef H5//#ifndef APP-PLUSconsole.log("执行md5值计算MP");const fs = uni.getFileSystemManager();fs.readFile({filePath: file, // 文件路径encoding: 'base64',success: (res) => {const binary = uni.base64ToArrayBuffer(res.data); // 将 base64 转换为 ArrayBufferconst spark = new SparkMD5.ArrayBuffer();spark.append(binary);resolve(spark.end());},fail: reject,});//#endif//#endif//#ifdef APP-PLUSconsole.log("执行md5值计算APP");plus.io.resolveLocalFileSystemURL(file, (entry) => {entry.file((fileObj) => {const reader = new plus.io.FileReader();reader.readAsDataURL(file);reader.onloadend = (evt) => {const binary = uni.base64ToArrayBuffer(evt.target.result); // 将 base64 转换为 ArrayBufferconst spark = new SparkMD5.ArrayBuffer();spark.append(binary);resolve(spark.end());};reader.onerror = reject;});}, reject);//#endif});
}/*** 获取文件MD5名称* @param {string} file 文件路径* @returns {Promise<string>} MD5文件名*/
async function getMD5FileName(file) {const md5 = await calculateMD5(file);console.log(md5)return;const fileType = file.substring(file.lastIndexOf("."));return `${md5}${fileType}`;
}/*** 文件上传* @param {Object} url* @param {Object} filePath* @param {Object} formData* @returns {Promise<string|null>} */
function uploadFile(url, filePath, formData) {return new Promise((resolve, reject) => {uni.uploadFile({url: url,filePath: filePath,name: 'file',formData: formData,success: (res) => {if (res.statusCode === 200) {resolve(res);} else {reject(new Error(`上传失败,状态码:${res.statusCode}, 响应信息:${res.data}`));}},error: (err) => {console.log("图片上传失败=》" + res)reject(err);},});});
}
export {putObjectAutoPath
};

python实现的

#!/usr/bin/env python
# coding=utf-8
import jsonfrom sts.sts import Sts
import hashlib
import hmac
import base64
import time
from datetime import datetime, timedelta#腾讯云 secret_id
secret_id = ''
#腾讯云 secret_key
secret_key = ''
#bucketId 储存桶ID
bucket = ''
#存储桶所在地域
region = ''def get_temporary_credential():"""获取临时密钥:return:"""config = {# 请求URL,域名部分必须和domain保持一致# 使用外网域名时:https://sts.tencentcloudapi.com/# 使用内网域名时:https://sts.internal.tencentcloudapi.com/# 'url': 'https://sts.tencentcloudapi.com/',# # 域名,非必须,默认为 sts.tencentcloudapi.com# # 内网域名:sts.internal.tencentcloudapi.com# 'domain': 'sts.tencentcloudapi.com',# 临时密钥有效时长,单位是秒'duration_seconds': 1800,'secret_id': secret_id,# 固定密钥'secret_key': secret_key,# 设置网络代理# 'proxy': {#     'http': 'xx',#     'https': 'xx'# },# 换成你的 bucket'bucket': bucket,# 换成 bucket 所在地区'region': region,# 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径# 例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)'allow_prefix': ['*'],# 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923'allow_actions': [# 简单上传'name/cos:PutObject','name/cos:PostObject',# 分片上传'name/cos:InitiateMultipartUpload','name/cos:ListMultipartUploads','name/cos:ListParts','name/cos:UploadPart','name/cos:CompleteMultipartUpload'],# # 临时密钥生效条件,关于condition的详细设置规则和COS支持的condition类型可以参考 https://cloud.tencent.com/document/product/436/71306# "condition": {#     "ip_equal":{#         "qcs:ip":[#             "10.217.182.3/24",#             "111.21.33.72/24",#         ]#     }# }}try:sts = Sts(config)response = sts.get_credential()print(response)# 添加新的属性response['bucket'] = bucketresponse['region'] = regionreturn responseexcept Exception as e:raise Exception("腾讯OSS临时密钥获取异常!")def get_bucketAndRegion():"""获取bucket 桶id 和region地域:return:"""data = {"bucket": bucket,"region": region}return datadef get_temporary_credential_self_upload(keyPath):"""获取腾讯云oss凭证 适用于POST上传请求【不依赖腾讯SDK】"""#获取临时签名credentials_data = get_temporary_credential().get("credentials")tmp_secret_id = credentials_data.get("tmpSecretId")tmp_secret_key = credentials_data.get("tmpSecretKey")session_token = credentials_data.get("sessionToken")# 开始计算凭证cos_host = f"{bucket}.cos.{region}.myqcloud.com"cos_key = keyPathnow = int(time.time())exp = now + 900q_key_time = f"{now};{exp}"q_sign_algorithm = 'sha1'# 生成上传要用的 policypolicy = {'expiration': (datetime.utcfromtimestamp(exp)).isoformat() + 'Z','conditions': [{'q-sign-algorithm': q_sign_algorithm},{'q-ak': tmp_secret_id},{'q-sign-time': q_key_time},{'bucket': bucket},{'key': cos_key},]}policy_encoded = base64.b64encode(json.dumps(policy).encode()).decode()# 步骤一:生成 SignKeysign_key = hmac.new(tmp_secret_key.encode(), q_key_time.encode(), hashlib.sha1).hexdigest()# 步骤二:生成 StringToSignstring_to_sign = hashlib.sha1(json.dumps(policy).encode()).hexdigest()# 步骤三:生成 Signatureq_signature = hmac.new(sign_key.encode(), string_to_sign.encode(), hashlib.sha1).hexdigest()return {'cosHost': cos_host,'cosKey': cos_key,'policy': policy_encoded,'qSignAlgorithm': q_sign_algorithm,'qAk': tmp_secret_id,'qKeyTime': q_key_time,'qSignature': q_signature,'securityToken': session_token  # 如果 SecretId、SecretKey 是临时密钥,要返回对应的 sessionToken 的值}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/368015.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3单个页面进行防抖节流

防抖 <template><button id"submitButton" ref"submitButton">GET</button> </template><script lang"ts" setup> import { ref, onMounted } from vue;// 防抖函数 function debounce(func: () > void, dela…

Python从入门到放弃——浮点型变量

浮点型变量 前言 上一篇文章我们研究了整数类型变量&#xff0c;本次我们来开始研究一下浮点类型变量。 浮点类型 浮点数在计算机编程中扮演着重要的角色。它们是一种特殊的数据类型&#xff0c;用于存储和处理小数或实数。在Python中&#xff0c;浮点数是由小数点分隔的…

[数据结构] --- 树

1 树的基本概念 1.1 树的定义 树是n(n>0)个结点的有限集。当 n 0 时&#xff0c;称为空树。在任意一棵树非空树中应满足&#xff1a; (1) 有且仅有一个特定的称为根 (root) 的结点&#xff1b; (2) 当 n > 1 时&#xff0c;其余结点可分为m(m>0)个互不相交的有限集…

【Unity 3D角色移动】

【Unity 3D角色移动】 在Unity 3D中实现角色移动通常涉及到几个关键步骤&#xff0c;包括设置角色的物理属性、处理输入、更新角色的位置以及动画同步。下面是实现基本3D角色移动的步骤和示例代码&#xff1a; 步骤1&#xff1a;设置角色的物理属性 角色通常使用Character Co…

单目相机减速带检测以及测距

单目相机减速带检测以及测距项目是一个计算机视觉领域的应用&#xff0c;旨在使用一个摄像头&#xff08;单目相机&#xff09;来识别道路上的减速带&#xff0c;并进一步估计车辆与减速带之间的距离。这样的系统对于智能驾驶辅助系统&#xff08;ADAS&#xff09;特别有用&…

【JavaWeb】利用IntelliJ IDEA 2024.1.4 +Tomcat10 搭建Java Web项目开发环境(图文超详细)

1、启动IntelliJ idea 2024.1.4 在欢迎页面&#xff0c;请确认好版本。因为不同的版本&#xff0c;搭建项目过程不太一样。 点击&#xff0c;新建项目。如图&#xff1a; 2、新建项目 在新建项目界面&#xff0c;选择java&#xff0c;在右侧信息模块内&#xff0c;根据个人情…

关于ant design vue 使用Modal无法关闭弹窗的解决思路

文章目录 1: 出现问题的版本2.出现问题&#xff08;1&#xff09;ant design 的问题&#xff08;2&#xff09;poina的提示报错 3.正确版本总结 1: 出现问题的版本 "ant-design-vue": "^3.2.20", "pinia": "^2.1.7", "vue"…

Ubuntu18.04新安装--无网络连接、重启黑屏解决教程

一、安装Ubuntu Ubuntu安装需要U盘作为启动盘&#xff0c;在目前教新的电脑中选中GPT作为分区&#xff0c;制作启动盘&#xff0c;其中在安装双系统Ubuntu时&#xff0c;以自定义格式作为存储空间。详细安装过程以以及如何分区请参考下列链接&#xff1a;内含详细安装过程&…

如何在Lazada平台快速出单?测评助力商家突破销量瓶颈

Lazada在短短的几年里已经发展成了东南亚地区最大的在线购物网站之一 &#xff0c;很多商家也想要在这样一个大的跨境平台上发展。那么&#xff0c;对于希望在Lazada平台上大展拳脚的商家而言&#xff0c;出单是否容易呢? ​一、Lazada出单容易吗? Lazada出单的难易程度并非…

Simulink 模型生成 C 代码(四):比较模型仿真和生成代码的结果

接下来将验证生成的代码执行时在数值上等效于 Simulink 中建模的算法。您使用测试框架模型在普通模式下对 RollAxisAutopilot 进行仿真&#xff0c;并在 SIL 模式下进行仿真&#xff0c;然后使用仿真数据检查器比较这两个仿真。 要测试生成的代码&#xff0c;您可以运行软件在…

Kubernetes基于helm安装 harbor

Kubernetes基于helm安装 harbor 之前harbor的安装都是借助docker完成一键安装部署&#xff0c;安装完成之后harbor组件均运行到一台机器上面&#xff0c;本文实践harbor在k8s环境中的部署。 准备工作 根据harbor官方要求&#xff1a; Kubernetes cluster 1.20Helm v3.2.0 …

SpringMVC基础详解

文章目录 一、SpringMVC简介1、什么是MVC2、MVC架构模式与三层模型的区别3、什么是SpringMVC 二、HelloWorld程序1、pom文件2、springmvc.xml3、配置web.xml文件4、html文件5、执行Controller 三、RequestMapping注解1、value属性1.1、基础使用1.2、Ant风格&#xff08;模糊匹配…

如何清理电脑内存?让电脑运行如飞!

电脑内存&#xff08;RAM&#xff09;的清理对于维持系统的流畅运行至关重要。随着使用时间的增加&#xff0c;系统内存会被各种应用程序和后台进程占用&#xff0c;导致系统响应变慢&#xff0c;甚至出现卡顿现象。通过有效地清理内存&#xff0c;可以提升电脑的性能&#xff…

数据库安全:MySQL权限体系划分与实战操作

「作者简介」&#xff1a;冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础著作 《网络安全自学教程》&#xff0c;适合基础薄弱的同学系统化的学习网络安全&#xff0c;用最短的时间掌握最核心的技术。 这一章节我们需…

网络基础:OSPF 协议

OSPF&#xff08;Open Shortest Path First&#xff09;是一种广泛使用的链路状态路由协议&#xff0c;用于IP网络中的内部网关协议&#xff08;IGP&#xff09;。OSPF通过在网络中的所有路由器之间交换路由信息&#xff0c;选择从源到目的地的最优路径。OSPF工作在OSI模型的第…

优化页面加载时间

注&#xff1a;机翻&#xff0c;未校对。 本文年代久远&#xff0c;除了少部分不合时宜的&#xff0c;其他仍有借鉴意义。 Optimizing Page Load Time 优化页面加载时间 It is widely accepted that fast-loading pages improve the user experience. In recent years, many …

【Elasticsearch】Elasticsearch动态映射与静态映射详解

文章目录 &#x1f4d1;前言一、Elasticsearch 映射概述1.1 什么是映射&#xff1f;1.2 映射的分类 二、动态映射2.1 动态映射的定义2.2 动态映射的优点2.3 动态映射的缺点2.4 动态映射的应用场景2.5 动态映射的配置示例 三、静态映射3.1 静态映射的定义3.2 静态映射的优点3.3 …

Zookeeper:Zookeeper集群角色

文章目录 一、Leader选举二、Zookeeper集群角色 一、Leader选举 Serverid&#xff1a;服务器ID&#xff1b;比如有三台服务器&#xff0c;编号越大在选择算法中的权重越大。Zxid&#xff1a;数据ID&#xff1b;服务器中存放的最大数据ID&#xff0c;值越大说明数据越新&#x…

JS(JavaScript) 数据校验 正则表达式

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

摸鱼大数据——Spark Core——RDD的基本介绍和如何构建RDD

1、什么是RDD RDD&#xff1a;英文全称Resilient Distributed Dataset&#xff0c;叫做弹性分布式数据集&#xff0c;代表一个不可变、可分区、里面的元素可并行计算的分布式的抽象的数据集合。 Resilient弹性&#xff1a;RDD的数据可以存储在内存或者磁盘当中&#xff0c;RDD…