Python 实现上传阿里云 OSS

Python 实现上传阿里云 OSS,第1张

概述背景首先,我要实现在某社区上发帖,该社区的图片服务器使用到了阿里云的OSS,因此通过抓包分析和查阅阿里云文档,自己用Python实现图片上传。也就是说我并不是很正式的使用OSS,毕竟阿里云也有自己的SDK,这里仅是使用临时权限上传到别人的服务器上。当然本文的签名生成也是可以正确 背景

首先,我要实现在某社区上发帖,该社区的图片服务器使用到了阿里云的 OSS,因此通过抓包分析和查阅阿里云文档,自己用 Python 实现图片上传。也就是说我并不是很正式的使用 OSS,毕竟阿里云也有自己的 SDK,这里仅是使用临时权限上传到别人的服务器上。

当然本文的签名生成也是可以正确使用的。一、上传准备

向社区服务器发起请求,社区服务器上再在阿里云上创建临时权限,下发密钥等。

因为不同的服务有不同的请求方式,所以这里略过。仅说明一下据笔者估计,可能会得到的一些通用的东西。注意:文章后续包含 self.upload_info 都是在这里获取到的数据。

这一步可以获取到例如以下的数据(已经过处理):

{    "data": {        "fileInfo": [{                "name": "aa1236c04949d3e83904a4c705a40dad.jpeg",                "md5": "123e5ddb72f59c123549ec4c970e8123",                "uploadfilename": "picture/2021/0114/12/[email protected]"            }        ],        "uploadInfo": {            "accessKeySecret": "BPaackBZ6cvdaGoa6a1MaPtXv81iqKNSu7Ro9MYHVF8k",            "accessKeyID": "STS.NTZnWEZjYQeaHF3gkocgypGnW",            "securityToken": "CAIS6gJ1q6Ft5B2yfSjIr5fPJe3xl7V45ercilang2s6bahVv4LFtTz2IH1Nf3hpBu8ftfUxm21X5vYblq4pF8AbFRyUNTz6cgXQt1HPWZHIaaDox2Zt6vT8a6z6aXPS2MvVfJ+2Lrf0ceusbFbpjzJ6xaCAGxypQ12iN+/u6/tgdc9FZhSkSjBECdxKXBaaavUXLnzML/2gHwf3i27Ldipercilanl05aaJoPmV4QGMi0bhmK1H5db6JZ+gZtFveZBkSJK02eV5e+3Z0SvM9gJHs/0u1vUDoW6e4pbEWR4Lv0rYY7qExYE3JgIkaqQwLPaa8qnLzaYm58SKx9Wt20oVbL8TUTzSS6LYmZCbRbPzZotlLueiYyqQiOriaael71kWBlsALx5PdtYbLXt9NAchUDmyknx8pQmRM178FPHegf9pjMulnwzyjtOOJkmSRbKCyjofOZI6YE4uOgQfwWv7aKgCfhzY/r3SnzdFJxqAAZDFAC9suvMP2bwttEv7QAIp2+3C21QweFVehAABoUJsmLbRkM7Ki4AjGyjOY1FHZgXap+3d5/0roeuaVIFs2G0tFVgTNU4y+zxfYqDR7e80SEP7+LTjxtCybGeXkw+XZYWZAlD4vo6cJzp6pBNCQW0eNt9k3qpwnFfylorgXHxh",            "expiration": "2021-01-14T04:52:49Z",            "endPoint": "http://oss-cn-hangzhou.aliyuncs.com",            "bucket": "test-oss-image",            "callbackUrl": "https://admin.test.com/callback/OssuploadSuccessCallback?needAttr=0&versionCode=007"        }    }}
二、计算签名官方文档

在Header中包含签名 - 对象存储 OSS - 阿里云

文档中对 Authorization 的计算方式如下:

Authorization = "OSS " + AccessKeyID + ":" + SignatureSignature = base64(hmac-sha1(AccessKeySecret,            VERB + "\n"            + Content-MD5 + "\n"             + Content-Type + "\n"             + Date + "\n"             + CanonicalizedOSSheaders            + CanonicalizedResource))

细节分析如下:

AccessKeySecret 表示签名所需的密钥。VERB 表示 http 请求的 Method,主要有 PUT、GET、POST、head、DELETE 等。\n 表示换行符。Content-MD5 表示请求内容数据的 MD5 值,对消息内容(不包括头部)计算 MD5 值获得 128 比特位数字,对该数字进行 base64 编码得出。该请求头可用于消息合法性的检查(消息内容是否与发送时一致),例如”eB5eJF1ptWaXm4biJsPyxw==”,也可以为空。详情请参见RFC2616 Content-MD5Content-Type 表示请求内容的类型,例如”application/octet-stream”,也可以为空。Date 表示此次 *** 作的时间,且必须为 GMT 格式,例如”Sun, 22 Nov 2015 08:16:38 GMT”。CanonicalizedOSSheaders 表示以 x-oss- 为前缀的 http header 的字典序排列。CanonicalizedResource 表示用户想要访问的 OSS 资源。

文章是几个月后补写的,应该没有什么大问题。我现在还是正常使用的。

这里我们看一下我们需要自行计算哪些东西。

AccessKeySecret 是访问某网址后,会提供给我们的(这里应该是该社区服务器向阿里云请求申请到了 OSS 临时权限然后返回给我们的)。VERB:请求方式我这里为 PUT。Content-Type:我这里是上传的 jpg 图片,都是固定的:"image/jpeg"。也就是说,我们需要计算或拼接 Content-MD5( --> 获取文件的 Content-MD5)、Date( --> 获取当前 GMT 时间的 Python 实现)、CanonicalizedOSSheadersCanonicalizedResource。Cotent_MD5 算法

获取文件的 Content-MD5 的 Python 代码:

def content_enCoding(path):    """    计算返回文件的 Content-MD5\n    其实就是计算文件md5时,不将计算所的得二进制转为hex编码,而是进行base64编码\n    :param path:文件的路径    :return:    """    h_md5 = hashlib.md5()    with open(path, 'rb') as f:        while True:            data = f.read(4096)            if not data:                break            h_md5.update(data)    content_base64 = base64.b64encode(h_md5.digest())    return content_base64.decode('utf-8')
获取当前 GMT 时间

获取当前 GMT 时间 Python 代码

def get_GMT_time():    GMT_FORMAT = '%a, %d %b %Y %H:%M:%s GMT'    return datetime.utcNow().strftime(GMT_FORMAT)
CanonicalizedOSSheaders 拼接

以下抠自官方 Python SDK:

def get_OSS_headers(headers):    canon_headers = []    for k, v in headers.items():        lower_key = k.lower()        if lower_key.startswith('x-oss-'):            canon_headers.append((lower_key, v))    canon_headers.sort(key=lambda x: x[0])    if canon_headers:        return '\n'.join(k + ':' + v for k, v in canon_headers) + '\n'    else:        return ''
CanonicalizedResource

这里,我们以我要上传图片为例:

from urllib.parse import unquoteresource = unquote(f"/{self.upload_info['bucket']}/{pic['uploadfilename']}")# 第一个是要上传的bucket,比如:test-oss-image# 第二个是要上传的图片名称,可获得形如以下的结果:# "/test-oss-image/picture/2021/0114/12/[email protected]"
注意这里不能有 url 编码(也就是说如果文件名有 url 编码,需要解码一下)。因为我这里要上传的图片名称是请求社区服务器后,服务器返回的数据规定的,含有 url 编码。计算签名

以下节选自我的代码,请结合注释自行分析所需的代码:

def _get_oss_signature(self, headers, resource):    headers_str = get_OSS_headers(headers) # 获取CanonicalizedOSSheaders    key = self.upload_info['accessKeySecret']    content_md5 = headers['Content-MD5'] # 计算过的 Content-MD5    content_type = headers['Content-Type'] # 上传的资源类型    date = headers['Date'] # 当前的GMT时间    raw_str = f"PUT\n{content_md5}\n{content_type}\n{date}\n{headers_str}"    # resource为拼接好的CanonicalizedResource,如:"/test-oss-image/picture/2021/0114/12/2326888_441a5ddb_9069_0365%401892x4096.jpeg"    raw_str = raw_str + unquote(resource)    # print(raw_str)    signature = base64.b64encode(hmac_sha1(key, raw_str)).decode('utf-8')    # print("签名为:", signature)    return signature
三、请求头构造分析

请求头一些关键必要的:AuthorizationDateContent-MD5Content-Typex-oss-callback-varx-oss-callbackx-oss-security-token
其中 AuthorizationDateContent-MD5Content-Type 在第一步骤中已经获取到了。而 x-oss-callback-varx-oss-callbackx-oss-security-token 是我们需要生成的。


x-oss-callback-varx-oss-callback:从名字上得知应该是社区服务器要使用的回调 url,其中的参数因服务器而异。而 x-oss-security-token 是我们上传图片前向服务器请求可以获取到的临时 token。

一些请求头的作用尚未分析。请求头生成

以下节选改编自我的代码,仅供参考分析,不同服务器可能有不同的参数,需自行解码抓包数据比对查看:

host_url = self.upload_info['endPoint'].split("//") # 服务器提供的数据# header信息生成host = self.upload_info['bucket'] + "." + host_url[1] # 拼接出要上传的hostcallback_var = {"x:var1": "false"}  # Todo 未知作用callback_var_b64 = b64_encode(dumps(callback_var)) # 这里用到的函数定义见后面callback_host = re.match(r'https?://(\w+\.\w+\.(com|cn))/.*', self.upload_info['callbackUrl'])[1] # 服务器提供的数据,拼接回调urlcall_back = {    "callbackBodyType": "application/Json",    "callbackHost": callback_host,    "callbackUrl": self.upload_info['callbackUrl'], # 服务器提供的数据    "callbackBody": "{\"bucket\":${bucket},\"object\":${object}}" # 因服务器而异}call_back_b64 = b64_encode(dumps(call_back))print("x-oss-callback:" + call_back_b64)upload_headers = {    "x-oss-callback-var": callback_var_b64,    "User-Agent": "aliyun-sdk-androID/2.9.2(linux/AndroID 11/Redmi%20K30%20;RKQ1.200826.001)", # 因机型而异    "Host": host,    "x-oss-callback": call_back_b64,    "x-oss-security-token": self.upload_info['securityToken'], # 服务器提供的数据    "Content-Type": "image/jpeg",    "Accept-EnCoding": "gzip"}

用到的函数:

def dumps(dic):      return Json.dumps(dic, separators=(',', ':'))  def b64_encode(text):      return base64.b64encode(text.encode('utf-8')).decode('utf-8')
Authorization

就是拼接一下签名:

# accessKeyID是服务器提供的authorization = f"OSS {self.upload_info['accessKeyID']}:{signature}"upload_headers['Authorization'] = authorization
三、发送请求

上传我们的图片到该社区的阿里云 OSS:

with open("D:\UserData\Desktop.jpg", "rb") as f:      upload_result = requests.put(url, data=f.read(), headers=upload_headers)# 接下来判断上传结果
这里应该使用 raw 上传文件。-> python-上传文件的几种方式 - 南方的墙 - 博客园获取最后的图片地址:我这里的情况的话,图片地址为社区指定的图片服务器域名(应该解析到了阿里云 OSS),后续即为第一步中的 uploadfilename。社区服务器上检验了图片域名,如果不这样的话,将会发帖失败,以供参考。

最后大功告成!美滋滋

总结

以上是内存溢出为你收集整理的Python 实现上传阿里云 OSS全部内容,希望文章能够帮你解决Python 实现上传阿里云 OSS所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1189489.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存