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 H5
		console.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-PLUS
		console.log("执行md5值计算MP");
		const fs = uni.getFileSystemManager();
		fs.readFile({
			filePath: file, // 文件路径
			encoding: 'base64',
			success: (res) => {
				const binary = uni.base64ToArrayBuffer(res.data); // 将 base64 转换为 ArrayBuffer
				const spark = new SparkMD5.ArrayBuffer();
				spark.append(binary);
				resolve(spark.end());
			},
			fail: reject,
		});
		//#endif
		//#endif

		//#ifdef APP-PLUS
		console.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 转换为 ArrayBuffer
					const 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 json

from 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'] = bucket
        response['region'] = region

        return response
    except Exception as e:
        raise Exception("腾讯OSS临时密钥获取异常!")


def get_bucketAndRegion():
    """
        获取bucket 桶id 和region地域
    :return:
    """
    data = {
        "bucket": bucket,
        "region": region
    }
    return data



def 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 = keyPath
    now = int(time.time())
    exp = now + 900
    q_key_time = f"{now};{exp}"
    q_sign_algorithm = 'sha1'

    # 生成上传要用的 policy
    policy = {
        '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()
    # 步骤一:生成 SignKey
    sign_key = hmac.new(tmp_secret_key.encode(), q_key_time.encode(), hashlib.sha1).hexdigest()

    # 步骤二:生成 StringToSign
    string_to_sign = hashlib.sha1(json.dumps(policy).encode()).hexdigest()

    # 步骤三:生成 Signature
    q_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.mfbz.cn/a/772013.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python机器学习】算法链与管道——通用的管道接口

Pipeline类补单可以用于预处理和分类&#xff0c;实际上还可以将任意数量的估计器连接在一起。例如&#xff0c;我们可以构建一个包含特征提取、特征选择、缩放和分类的管道&#xff0c;总共有4个步骤。同样的&#xff0c;最后一步可以用聚类或回归代替。 对于管道中估计器的唯…

【机器学习】Datawhale-AI夏令营分子性质AI预测挑战赛

参赛链接&#xff1a;零基础入门 Ai 数据挖掘竞赛-速通 Baseline - 飞桨AI Studio星河社区 一、赛事背景 在当今科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的深度和广度渗透到科研领域&#xff0c;特别是在化学及药物研发中展现出了巨…

警翼警用记录仪视频格式化后恢复方法

警翼是国内较大的一家警用记录仪厂商&#xff0c;此品牌我们恢复过很多&#xff0c;此次遇到的是一个典型的误格式化的情况&#xff0c;我们来看看误格式化后如何恢复。 故障存储: 32G卡/fat32 故障现象: 客户提供的信息是在交接设备后没有及时备份而做出了初始化设备的操…

fluwx插件实现微信支付

Flutter开发使用fluwx插件实现微信支付&#xff0c;代码量不多&#xff0c;复杂的是安卓和iOS的各种配置。 在 pubspec.yaml 文件中添加fluwx依赖 fluwx: ^4.5.5 使用方法 通过fluwx注册微信Api await Fluwx().registerApi(appId: wxea7a1c53d9e5849d, universalLink: htt…

机器人控制系列教程之Delta机器人动力学分析

动力学简介 机器人动力学分析是已知各运动构件的尺寸参数和惯性参数的情况下,求解末端运动状态与主驱动力矩之间的函数关系。 意义:对并联机器人动力学分析的意义体现在: 为伺服电机的选型提供理论依据;获得动力学参数为目标函数的最优问题做性能评价指标;为高精度控制提…

内容为王:揭秘顶尖品牌的内容营销制胜法宝

内容营销是当今互联网市场推广领域的热门话题&#xff0c;因为它可以帮助企业更好地与受众沟通、建立品牌口碑&#xff0c;增加销售量。 根据咱们何策网的资源库里的SocialBeta2024年最新《2024 内容营销 10 大趋势》的报告来看&#xff0c;品牌在未来内容营销中最应该注重的是…

2024亚太杯中文赛数学建模B题【洪水灾害的数据分析与预测】思路详解

2024 年第十四届 APMCM 亚太地区大学生数学建模竞赛 B题 洪水灾害的数据分析与预测 附件 train.csv 中提供了超过 100 万的洪水数据&#xff0c;其中包含洪水事件的 id、季风强度、地形排水、河流管理、森林砍伐、城市化、气候变化、大坝质量、淤积、农业实践、侵蚀、无效防灾、…

Unity 之基于URP使用UniStorm Weather System天气系统

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity 之基于URP使用UniStorm Weather System天气系统 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、…

Linux和mysql中的基础知识

cpu读取的指令大部分在内存中&#xff08;不考虑缓存&#xff09; 任何程序在运行之前都的加入到内存。 eip->pc指针&#xff0c;指明当前指令在什么位置。 代码大概率是从上往下执行的&#xff0c;基于这样的基本理论。既可以将一部分指令加载到CPU对应的缓存中&#xf…

智能猫砂盆到底哪家好用?自费实测聚宠、糯雪、CEWEY真实反馈!

快到夏天了&#xff0c;是不是还有人因为没挑选到喜欢的智能猫砂盆而苦恼着&#xff1f;太便宜怕不好用&#xff0c;太贵怕质量比不上价格。来来去去拖到现在还没决定&#xff0c;我作为养了四年猫的资深铲屎官&#xff0c;今天就来给大家传授经验&#xff0c;关于我是怎么从好…

从源码到应用:直播电商系统与短视频带货APP开发指南

本篇文章&#xff0c;笔者将从源码到应用&#xff0c;详细探讨如何开发一个直播电商系统和短视频带货APP。 一、系统架构设计 在开始开发之前&#xff0c;首先需要对系统进行整体架构设计。一个完整的直播电商系统和短视频带货APP主要包括以下几个模块&#xff1a; 1.用户管理…

Android12 MultiMedia框架之MediaExtractorService

上节学到setDataSource()时会创建各种Source&#xff0c;source用来读取音视频源文件&#xff0c;读取到之后需要demux出音、视频、字幕数据流&#xff0c;然后再送去解码。那么负责进行demux功能的media extractor模块是在什么时候阶段创建的&#xff1f;这里暂时不考虑APP创建…

UE5.4新功能 - Texture Graph上手简介

TextureGraph是UE5.4还在实验(Experimental)阶段的新功能&#xff0c;该功能旨在材质生成方面达到类似Subtance Designer的效果&#xff0c;从而程序化的生成一些纹理。 本文就来简要学习一下。 1.使用UE5.4或以上版本&#xff0c;激活TextureGraph插件 2.内容视图中右键找到…

day11_homework_need2submit

Homework 编写—个将ts或mp4中视频文件解码到yuv的程序 yuv数据可以使用如下命令播放: ffplay -i output yuv-pix_fmt yuv420p-s 1024x436 要求: ffmpeg解析到avpacket并打印出pts和dts字段完成解码到avframe并打印任意字段完成yuv数据保存 // teminal orders on bash cd ex…

6 矩阵相关案例

矩阵计算在CUDA中的应用是并行计算领域的典型场景 &#xff1b; 矩阵算法题通常涉及线性代数的基础知识&#xff0c;以及对数据结构和算法的深入理解。解决这类问题时&#xff0c;掌握一些核心思想和技巧会非常有帮助。以下是一些常见的矩阵算法题解题思想&#xff1a; 动态规划…

stm32——定时器级联

在STM32当中扩展定时范围&#xff1a;单个定时器的定时长度可能无法满足某些应用的需求。通过级联&#xff0c;可以实现更长时间的定时&#xff1b;提高定时精度&#xff1a;能够在长定时的基础上&#xff0c;通过合理配置&#xff0c;实现更精细的定时控制&#xff1b;处理复杂…

Postman工具基本使用

一、安装及基本使用 安装及基本使用参见外网文档&#xff1a;全网最全的 postman 工具使用教程_postman使用-CSDN博客 建议版本&#xff1a;11以下&#xff0c;比如10.x.x版本。11版本以后貌似是必须登录使用 二、禁止更新 彻底禁止postman更新 - 简书 host增加&#xff1…

vector与list的简单介绍

1. 标准库中的vector类的介绍&#xff1a; vector是表示大小可以变化的数组的序列容器。 就像数组一样&#xff0c;vector对其元素使用连续的存储位置&#xff0c;这意味着也可以使用指向其元素的常规指针上的偏移量来访问其元素&#xff0c;并且与数组中的元素一样高效。但与数…

【计算机毕业设计】026基于微信小程序的原创音乐

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

记录OSPF配置,建立邻居失败的过程

1.配置完ospf后&#xff0c;在路由表中不出现ospf相关信息 [SW2]ospf [SW2-ospf-1]are [SW2-ospf-1]area 0 [SW2-ospf-1-area-0.0.0.0]net [SW2-ospf-1-area-0.0.0.0]network 0.0.0.0 Jul 4 2024 22:11:58-08:00 SW2 DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.2011.5.25 .1…