本文介绍使用OSS Python SDK的常见问题及解决方法。

OSS Python SDK分片上传失败

解决方法如下:

  • 先确认是直接上传到OSS,还是通过其他proxy传输到OSS(类似CDN)。如果经过CDN再上传到OSS,需要在OSS中配置跨域的HTTP Header,例如Access-Control-Allow-OriginAccess-Control-Allow-MehtodsAccess-Control-Allow-Headers等,并暴露ETag。更多信息,请参见PutBucketcors
  • 如果是网络超时导致上传失败,建议使用断点续传来替代普通上传。断点续传支持并行上传以及自定义分片大小。如果捕获到异常,需要详细查看并分析捕获到的SDK异常信息。
  • 清理上传失败的碎片文件再重新上传。
如果以上方案仍旧没有解决您的问题,需要将以下信息提供给阿里云:
  • SDK返回异常中的requestID。
  • 客户端部署tcpdump,然后重新运行代码上传,并保存抓包。
    tcpdump -i <网卡出口名称> -s0 host <访问oss的域名> -w faild.pcap

同台机器使用ossutil上传下载很快,而使用Python SDK上传下载很慢

  • 错误原因

    ossutil工具基于Go SDK进行开发,并发上传的性能较好。如果使用Python SDK上传、下载时性能远不如ossutil,通常是因为没有正确安装crcmod 。

  • 解决方法

    关于安装crcmod的更多信息,请参见安装Python SDK。如仍未能解决您的问题,请提交工单。

在Centos机器上调用SDK中的分片上传函数正常,但在Ubuntu机器上总是报403错误。

客户端部署tcpdump抓包,然后通过tcp报文排查是否由于Header信息不正确导致计算签名与服务端不匹配。

POST /ttsservice%2Fpasswd?uploadId=D468E486D1D94D90A1AB8885A4E32AE0 HTTP/1.1
Host: rokid.oss-cn-hangzhou.aliyuncs.com
Accept-Encoding: identity
Accept: text/html
Content-Length: 137
date: Sat, 29 Dec 2018 07:32:34 GMT
authorization: OSS LTAIknFr:r2KPR0y4E0G5tnU/MYdcvXHP****
Content-Type: application/x-www-form-urlencoded
User-Agent: aliyun-sdk-python/2.6.0(Linux/4.4.0-31-generic/x86_64;3.4.3)

<CompleteMultipartUpload><Part><PartNumber>1</PartNumber><ETag>"3195544E19D99658706D5****"</ETag></Part></CompleteMultipartUpload>HTTP/1.1 403 Forbidden

Server: AliyunOSS
Date: Sat, 29 Dec 2018 07:33:43 GMT
Content-Type: application/xml
Content-Length: 1122
Connection: keep-alive
x-oss-request-id: 5C2723573183****
x-oss-server-time: 0

<?xml version="1.0" encoding="UTF-8"?>
<Error>
 <Code>SignatureDoesNotMatch</Code>
 <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
 <RequestId> 5C2723573183A12D </RequestId>
 <HostId>rokid.oss-cn-hangzhou.aliyuncs.com</HostId>
 <OSSAccessKeyId> LTAXXX </OSSAccessKeyId>
 <SignatureProvided>r2KPR0y4E0G5tnU/MYdc****</SignatureProvided>
 <StringToSign>POST
application/x-www-form-urlencoded
Sat, 29 Dec 2018 07:32:34 GMT
/rokid/ttsservice/passwd?uploadId=D468E486D1D94D90A1AB8885A4E3****</StringToSign>
 <StringToSignBytes>50 4F 53 54 0A 0A 61 70 70 6C 69 63 61 74 69 6F 6E 2F 78 2D 77 77 77 2D 66 6F 72 6D 2D 75 72 6C 65 6E 63 6F 64 65 64 0A 53 61 74 2C 20 32 39 20 44 65 63 20 32 30 31 38 20 30 37 3A 33 32 3A 33 34 20 47 4D 54 0A 2F 72 6F 6B 69 64 2D 6F 70 73 2D 6D 6F 64 65 6C 2F 74 74 73 73 65 72 76 69 63 65 2F 70 61 73 73 77 64 3F 75 70 6C 6F 61 64 49 64 3D 44 34 36 38 45 34 38 36 44 31 44 39 34 44 39 30 41 31 41 42 38 38 38 35 41 34 45 33 32 41 45 30 </StringToSignBytes>
</Error>

如果服务端收到的签名(Signature)和客户端计算的签名信息不一致,说明请求的内容已被改动,建议使用HTTPS的方式上传。

以上是客户端抓取的报文信息。请将获取的请求头信息带入以下脚本,并将计算结果与SDK进行比较。
import base64
import hmac
import sha
mac = hmac.new("<Secretkey>","POST\n\napplication/x-www-form-urlencoded\nSat, 29 Dec 2018 07:32:34 GMT\n/rokid/ttsservice/passwd?uploadId=D468E486D1D94D90A1AB8885A4E3****", sha)
Signature = base64.b64encode(mac.digest())
print(Signature)

如果抓包数据和脚本计算的结果一致,则说明SDK计算正确。如果抓包数据和脚本计算的结果不一致,可能是因为SDK在Ubuntu平台编译的适配问题导致MD5值不一样。

在Mac环境中用Python启动多线程并在子线程中使用OSS时,import tensorflow会报错,没有import tensorflow则不会报错。如果没有启动多线程,使用OSS时import tensorflow不会报错。

Python多线程运行时,报如下错误:
objc[2483]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[2483]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.

解决方法:

添加环境变量 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES,如下图所示: fig_python_107749_02

更多信息,请参见macOS 10.13系统使用多线程的错误说明

如何添加重试策略

Python SDK自身没有重试机制。在网络状况不好的情况下,可能会出现请求失败的情况。此时,建议您参考如下代码自行添加重试策略。

def test_get_object():
    MAX_RETRIES = 3
    retry_count = 0
    while True:
        try:
            retry_count += 1
            # yourObjectName表示不包含Bucket名称在内的OSS文件的完整路径,例如abc/efg/example.jpg。
            # yourFileName表示下载到本地文件的完整路径,例如/users/local/example.jpg。
            bucket.get_object_to_file("yourObjectName", "yourFileName")
            break
        except Exception:
            if retry_count >= MAX_RETRIES:
                raise