我需要创建一个CSV并将其上传到S3存储桶。由于我正在动态创建文件,如果我可以在创建它时直接将其写入S3存储桶而不是在本地写入整个文件,然后在最后上传文件,那会更好。
有办法做到这一点吗?我的项目是用Python编写的,而且我对这门语言还是比较陌生的。这是我到目前为止所尝试的:
import csv
import csv
import io
import boto
from boto.s3.key import Key
conn = boto.connect_s3()
bucket = conn.get_bucket('dev-vs')
k = Key(bucket)
k.key = 'foo/foobar'
fieldnames = ['first_name', 'last_name']
writer = csv.DictWriter(io.StringIO(), fieldnames=fieldnames)
k.set_contents_from_stream(writer.writeheader())
我收到此错误:BotoClientError:s3不支持分块传输
更新:我找到了一种直接写入S3的方法,但我找不到清除缓冲区的方法,却没有实际删除我已编写的行。所以,例如:
conn = boto.connect_s3()
bucket = conn.get_bucket('dev-vs')
k = Key(bucket)
k.key = 'foo/foobar'
testDict = [{
"fieldA": "8",
"fieldB": None,
"fieldC": "888888888888"},
{
"fieldA": "9",
"fieldB": None,
"fieldC": "99999999999"}]
f = io.StringIO()
fieldnames = ['fieldA', 'fieldB', 'fieldC']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
k.set_contents_from_string(f.getvalue())
for row in testDict:
writer.writerow(row)
k.set_contents_from_string(f.getvalue())
f.close()
将3行写入文件,但是我无法释放内存来写大文件。如果我添加:
f.seek(0)
f.truncate(0)
到循环,然后只写入文件的最后一行。有没有办法在不删除文件中的行的情况下释放资源?
答案 0 :(得分:24)
我确实找到了我的问题的解决方案,我将在此处发布以防其他人感兴趣。我决定在分段上传中将其作为部分。您无法流式传输到S3。还有一个软件包可以将您的流媒体文件更改为我使用的分段上传:Smart Open。
<span>
答案 1 :(得分:4)
根据docs,可能
s3.Object('mybucket', 'hello.txt').put(Body=open('/tmp/hello.txt', 'rb'))
所以我们可以通常的方式使用StringIO
更新:smart_open lib是更好的解决方案
答案 2 :(得分:1)
我们试图将文件内容作为Django请求中的InMemoryUploadedFile对象上传到s3。我们结束了以下操作,因为我们不想在本地保存文件。希望对您有所帮助:
@action(detail=False, methods=['post'])
def upload_document(self, request):
document = request.data.get('image').file
s3.upload_fileobj(document, BUCKET_NAME,
DESIRED_NAME_OF_FILE_IN_S3,
ExtraArgs={"ServerSideEncryption": "aws:kms"})
答案 3 :(得分:1)
这是一个使用 boto3
import boto3
import io
session = boto3.Session(
aws_access_key_id="...",
aws_secret_access_key="..."
)
s3 = session.resource("s3")
buff = io.BytesIO()
buff.write("test1\n".encode())
buff.write("test2\n".encode())
s3.Object(bucket, keypath).put(Body=buff.getvalue())
答案 4 :(得分:0)
要将字符串写到S3对象,请使用:
s3.Object('my_bucket', 'my_file.txt').put('Hello there')
因此将流转换为字符串,您就在那里。
答案 5 :(得分:0)
在GitHub smart_open
问题(#82)中提到了一个有趣的代码解决方案,我一直想尝试一下。为了后代而在此处复制粘贴...看来boto3
是必需的:
csv_data = io.BytesIO()
writer = csv.writer(csv_data)
writer.writerows(my_data)
gz_stream = io.BytesIO()
with gzip.GzipFile(fileobj=gz_stream, mode="w") as gz:
gz.write(csv_data.getvalue())
gz_stream.seek(0)
s3 = boto3.client('s3')
s3.upload_fileobj(gz_stream, bucket_name, key)
此特定示例正在流式传输到压缩的S3密钥/文件,但似乎是通用方法-将boto3
S3客户端的upload_fileobj()
方法与目标流而非文件结合使用-应该可以。