CSV文件从缓冲区上传到S3

cam*_*ack 6 django amazon-s3 python-3.x boto3

我正在尝试将Django模型中的内容作为csv文件上传。我不想将文件保存在本地,而是将其保留在缓冲区中并上传到s3。当前,此代码不会按原样错误,并且会正确上传文件,但是文件为空。

file_name='some_file.csv'
fields = [list_of_fields]
header = [header_fields]

buff =  io.StringIO()
writer = csv.writer(buff, dialect='excel', delimiter=',')
writer.writerow(header)
for value in some_queryset:
    row = []
    for field in fields:
        # filling in the row
    writer.writerow(row)

# Upload to s3
client = boto3.client('s3')
bucket = 'some_bucket_name'
date_time = datetime.datetime.now()
date = date_time.date()
time = date_time.time()
dt = '{year}_{month}_{day}__{hour}_{minute}_{second}'.format(
    day=date.day,
    hour=time.hour,
    minute=time.minute,
    month=date.month,
    second=time.second,
    year=date.year,
)
key = 'some_name_{0}.csv'.format(dt)

client.upload_fileobj(buff, bucket, key)
Run Code Online (Sandbox Code Playgroud)

如果我使用缓冲区的内容,那肯定是在写它:

content = buff.getvalue()
content.encode('utf-8')
print("content: {0}".format(content)) # prints the csv content
Run Code Online (Sandbox Code Playgroud)

编辑:我正在使用在缓冲区中创建的zip文件做类似的事情:

with zipfile.ZipFile(buff, 'w') as archive:
Run Code Online (Sandbox Code Playgroud)

写入存档(添加我正在生成的pdf文件),完成后,我执行此操作:buff.seek(0)这似乎是必要的。如果我在上面做类似的事情,它将出错:Unicode-objects must be encoded before hashing

Tho*_*ite 11

好吧,无视我先前的答案,我发现了实际的问题。

根据该upload_fileobj函数的boto3文档,第一个参数(Fileobj)需要实现一个read()方法,该方法返回字节:

Fileobj(类似文件的对象)-要上传的类似文件的对象。至少,它必须实现read方法,并且必须返回字节。

read()一个对函数_io.StringIO对象返回一个字符串,而不是字节。我建议将StringIO对象替换为BytesIO对象,并添加必要的编码和解码。

这是一个最小的工作示例。这不是最有效的解决方案-基本思想是将内容复制到第二个BytesIO对象。

import io
import boto3
import csv

buff = io.StringIO()

writer = csv.writer(buff, dialect='excel', delimiter=',')
writer.writerow(["a", "b", "c"])

buff2 = io.BytesIO(buff.getvalue().encode())

bucket = 'changeme'
key = 'blah.csv'

client = boto3.client('s3')
client.upload_fileobj(buff2, bucket, key)
Run Code Online (Sandbox Code Playgroud)

  • 我花了很长时间才找到这个,但它解决了我的问题。谢谢你!! (2认同)

小智 5

正如这里所解释的,使用方法 put_object 而不是 upload_fileobj 只会使用 io.STRINGIO 对象缓冲区完成这项工作。

所以在这里,为了匹配最初的示例:

client = boto3.client('s3')
client.upload_fileobj(buff2, bucket, key)
Run Code Online (Sandbox Code Playgroud)

会成为

client = boto3.client('s3')
client.put_object(Body=buff2, Bucket=bucket, Key=key, ContentType='application/vnd.ms-excel')
Run Code Online (Sandbox Code Playgroud)