使用Boto3将文件从内存上传到S3

16

这个问题已经被问了很多次,但是我的情况略有不同。我正在尝试创建一个Lambda函数,用于创建一个.html文件并将其上传到S3。当文件在磁盘上创建时,它可以像下面这样上传:

boto3.client('s3').upload_file('index.html', bucket_name, 'folder/index.html')

现在我必须在内存中创建文件,为此我首先尝试使用StringIO()。然而,.upload_file会抛出一个错误。

boto3.client('s3').upload_file(temp_file, bucket_name, 'folder/index.html')
ValueError: Filename must be a string`. 

所以我尝试使用.upload_fileobj(),但是我得到了错误TypeError:需要类似于字节的对象,而不是'str'

所以我尝试使用Bytesio(),它要求我先将str转换为bytes,所以我这样做了:

temp_file = BytesIO()
temp_file.write(index_top.encode('utf-8'))
print(temp_file.getvalue())
boto3.client('s3').upload_file(temp_file, bucket_name, 'folder/index.html')

但现在它只上传一个空文件,尽管 .getvalue() 明确显示它的内部确实有内容。

我做错了什么?

2个回答

23

如果您希望从内存中创建Amazon S3中的对象,请使用put_object()

import boto3

s3_client = boto3.client('s3')

html = "<h2>Hello World</h2>"

s3_client.put_object(Body=html, Bucket='my-bucket', Key='foo.html', ContentType='text/html')

那比我想象的简单多了。它甚至不需要整个StringIO()的麻烦。 - Herman

7

但现在它只上传一个空文件,尽管.getvalue()明确显示其中有内容。

当你写入文件缓冲区后,位置会停留在末尾。 当你上传缓冲区时,它从当前位置开始。 由于你在末尾,所以没有数据。 为了解决这个问题,你只需要在完成写入后添加seek(0)来将缓冲区重置回开头。你的代码应该是这样的:

temp_file = BytesIO()
temp_file.write(index_top.encode('utf-8'))
temp_file.seek(0)
print(temp_file.getvalue())
boto3.client('s3').upload_file(temp_file, bucket_name, 'folder/index.html')

2
请注意,如果您已经有了字符串,则可以直接将其传递到BytesIO构造函数中,这将使用给定的数据创建缓冲区,但将位置保留在0处,从而避免您在seek调用中输入所有这些字母。(当然,如果您需要在多个write调用中逐步构建字节缓冲区,则无法使用此方法。) - Jack Brounstein
1
就性能和稳定性而言,这个解决方案比另一个要好得多:upload_file使用分段上传,而put_object则不使用。 - ciurlaro
1
我把它改成了"upload_fileobj",结果非常顺利。 - Mithsew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接