[英]S3 Python - Multipart upload to s3 with presigned part urls
我尝试使用预先签名的部分URL进行分段上传,但未成功。
这是我遵循的过程(1-3在服务器端,4在客户端):
import boto3
from botocore.client import Config
s3 = boto3.client(
"s3",
region_name=aws.default_region,
aws_access_key_id=aws.access_key_id,
aws_secret_access_key=aws.secret_access_key,
config=Config(signature_version="s3v4")
)
upload = s3.create_multipart_upload(
Bucket=AWS_S3_BUCKET,
Key=key,
Expires=datetime.now() + timedelta(days=2),
)
upload_id = upload["UploadId"]
part = generate_part_object_from_client_submited_data(...)
part.presigned_url = s3.generate_presigned_url(
ClientMethod="upload_part",
Params={
"Bucket": AWS_S3_BUCKET,
"Key": upload_key,
"UploadId": upload_id,
"PartNumber": part.no,
"ContentLength": part.size,
"ContentMD5": part.md5,
},
ExpiresIn=3600, # 1h
HttpMethod="PUT",
)
将预签名的URL返回给客户端。
requests
上载零件。 part = receive_part_object_from_server(...)
with io.open(filename, "rb") as f:
f.seek(part.offset)
buffer = io.BytesIO(f.read(part.size))
r = requests.put(
part.presigned_url,
data=buffer,
headers={
"Content-Length": str(part.size),
"Content-MD5": part.md5,
"Host": "AWS_S3_BUCKET.s3.amazonaws.com",
},
)
当我尝试上传时,我要么得到:
urllib3.exceptions.ProtocolError:
('Connection aborted.', BrokenPipeError(32, 'Broken pipe'))
要么:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchUpload</Code>
<Message>
The specified upload does not exist. The upload ID may be invalid,
or the upload may have been aborted or completed.
</Message>
<UploadId>CORRECT_UPLOAD_ID</UploadI>
<RequestId>...</RequestId>
<HostId>...</HostId>
</Error>
即使上传仍然存在,我也可以列出它。
谁能告诉我我在做什么错?
您是否尝试了预签名的POST ? 这是适用于它的AWS Python参考: https : //docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-presigned-post.html
从客户端的角度看,这可能会解决代理限制,如果有的话:
作为最后的选择,您始终可以尝试使用旧的REST API,尽管我认为问题不在您的代码中,也不在boto3中: https ://docs.aws.amazon.com/AmazonS3/latest/dev/UsingRESTAPImpUpload .html
这是一个功能完全相同的命令实用程序,您可能想在尝试时提供它,看看它是否有效。 如果这样做的话,很容易找到您的代码与其代码之间的差异。 如果没有,我将仔细检查整个过程。 这是一个如何使用aws
命令行https://aws.amazon.com/premiumsupport/knowledge-center/s3-multipart-upload-cli/?nc1=h_ls上传文件的示例
其实是否可行。 也就是说,您可以使用aws s3命令来补充上传内容,然后我们需要集中精力使用分配的url。 您可以在此处检查网址的外观:
https://github.com/aws/aws-sdk-js/issues/468 https://github.com/aws/aws-sdk-js/issues/1603
这是js sdk,但那里的人谈论的是原始网址和参数,因此您应该能够发现自己的网址和有效网址之间的差异。
另一个选择是尝试使用此脚本,它使用js从网络浏览器使用指定的URL上传文件。
https://github.com/prestonlimlianjie/aws-s3-multipart-presigned-upload
如果可行,您可以检查通信并观察上载每个部分所使用的确切URL,您可以将其与系统生成的URL进行比较。
顺便说一句。 一旦有了用于分段上传的有效URL,就可以使用aws s3 presign url
获取分配的url,这应该使您仅使用curl
即可完成上传,从而完全控制上传过程。
预设网址方法
您可以在以下链接中研究适用于Python SDK(Boto3)的AWS S3预签名URL以及如何使用分段上传API。
转移经理方法
Boto3提供了用于通过S3管理各种类型的传输的接口,以自动管理分段和非分段上传。 为确保分段上传仅在绝对必要时才发生,可以使用multipart_threshold
配置参数。
尝试使用以下代码进行Transfer Manager方法:
import boto3
from boto3.s3.transfer import TransferConfig
import botocore
from botocore.client import Config
from retrying import retry
import sysdef upload(source, dest, bucket_name):
try:
conn = boto3.client(service_name="s3",
aws_access_key_id=[key],
aws_secret_access_key=[key],
endpoint_url=[endpoint],
config=Config(signature_version='s3')
config = TransferConfig(multipart_threshold=1024*20,
max_concurrency=3,
multipart_chunksize=1024*20,
use_threads=True)
conn.upload_file(Filename=source, Bucket=bucket_name,
Key=dest, Config=config)
except Exception as e:
raise Exception(str(e))def download(src, dest, bucket_name):
try:
conn = boto3.client(service_name="s3",
aws_access_key_id=[key],
aws_secret_access_key=[key],
endpoint_url=[endpoint],
config=Config(signature_version='s3')
config = TransferConfig(multipart_threshold=1024*20,
max_concurrency=3,
multipart_chunksize=1024*20,
use_threads=True)
conn.download_file(bucket=bucket_name, key=src,
filename=dest, Config=config)
except AWSConnectionError as e:
raise AWSConnectionError("Unable to connect to AWS")
except Exception as e:
raise Exception(str(e))if __name__ == '__main__':
upload(source, dest, bucket_name)
download(src, dest, bucket_name)
AWS STS方法
您还可以遵循AWS安全令牌服务(STS)方法来生成一组临时凭证来代替,以完成您的任务。
针对AWS STS方法尝试以下代码:
import json
from uuid import uuid4
import boto3
def get_upload_credentials_for(bucket, key, username):
arn = 'arn:aws:s3:::%s/%s' % (bucket, key)
policy = {"Version": "2012-10-17",
"Statement": [{
"Sid": "Stmt1",
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": [arn],
}]}
client = boto3.client('sts')
response = client.get_federation_token(
Name=username, Policy=json.dumps(policy))
return response['Credentials']
def client_from_credentials(service, credentials):
return boto3.client(
service,
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
def example():
bucket = 'mybucket'
filename = '/path/to/file'
key = uuid4().hex
print(key)
prefix = 'tmp_upload_'
username = prefix + key[:32 - len(prefix)]
print(username)
assert len(username) <= 32 # required by the AWS API
credentials = get_upload_credentials_for(bucket, key, username)
client = client_from_credentials('s3', credentials)
client.upload_file(filename, bucket, key)
client.upload_file(filename, bucket, key + 'bob') # fails
example()
MinIO Client SDK for Python方法
您可以使用MinIO Client SDK for Python,该SDK实现了更简单的API,避免了分段上传的具体细节。
例如,您可以使用简单的fput_object(bucket_name, object_name, file_path, content_type)
API来满足需求。
针对适用于Python的MinIO Client SDK尝试以下代码:
from minio import Minio
from minio.error import ResponseError
s3client = Minio('s3.amazonaws.com',
access_key='YOUR-ACCESSKEYID',
secret_key='YOUR-SECRETACCESSKEY')
# Put an object 'my-objectname' with contents from 'my-filepath'
try:
s3client.fput_object('my-bucketname', 'my-objectname', 'my-filepath')
except ResponseError as err:
print(err)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.