Django CSV文件从缓冲区上载到S3

Django CSV文件从缓冲区上载到S3,django,python-3.x,amazon-s3,boto3,Django,Python 3.x,Amazon S3,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=',') wri

我正在尝试将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)
如果我获取缓冲区的内容,它肯定在写入它:

content = buff.getvalue()
content.encode('utf-8')
print("content: {0}".format(content)) # prints the csv content
编辑:我正在对一个在缓冲区中创建的zip文件执行类似的操作:

with zipfile.ZipFile(buff, 'w') as archive:

写入归档文件(添加我正在生成的pdf文件),完成后,执行以下操作:
buff.seek(0)
,这似乎是必要的。如果我执行上面类似的操作,它将出错:
在散列之前必须对Unicode对象进行编码

您是否尝试过先调用buff.flush()?您完全合理的调试检查(调用getvalue())可能会产生一种错觉,认为buff已经写入,但如果您不调用它,它就不会被写入。

好的,不管我前面的回答,我发现了实际问题

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

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

\u io.StringIO
对象上的
read()
函数返回字符串,而不是字节。我建议将
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)

您可以使用类似于将输出重定向到S3的方法。

正如使用方法put\u object而不是upload\u fileobj所解释的那样,只需使用io.STRINGIO对象缓冲区即可

因此,在这里,要匹配初始示例:

client = boto3.client('s3')
client.upload_fileobj(buff2, bucket, key)
将成为

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

我试过,
buff.flush()
调用介于csv写入S3上传之间,S3上的csv文件仍然为空。请给出一个示例?我真的很感谢你的帮助,顺便说一句:)上面的例子为我返回了合理的数据-它可能只是编码不匹配。您是如何从s3读回文件的?为了清楚起见,在执行示例代码后,我使用AWS CLI将文件复制到本地目录以查看内容。如果您通过浏览器检索CSV,则默认情况下可能采用与utf-8不同的编码。如果您遵循我的方法(直接下载原始文件)并且内容正确,则只需找到适用于浏览器的正确编码。那么我的示例与您的代码之间唯一可能的区别就是您使用csv.writerow()编写的数据。我知道你的问题的全部目的是为了避免使用硬盘,但是你尝试过将写操作作为调试解决方案?我花了很长时间才找到这个问题,但它解决了我的问题。非常感谢。请阅读-总结是,这不是一个理想的方式来解决志愿者,可能会适得其反获得答案。请不要把这个添加到你的问题中。明白了@halfer现在我知道了。这是有道理的:)谢谢@khc,但我试着使用我已经投资的东西,而不是使用新的库:)