【问题标题】:CSV file upload from buffer to S3CSV 文件从缓冲区上传到 S3
【发布时间】:2018-01-23 18:58:43
【问题描述】:

我正在尝试将从 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-objects must be encoded before hashing

【问题讨论】:

标签: django python-3.x amazon-s3 boto3


【解决方案1】:

正如here 解释的那样,使用 put_object 而不是 upload_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')

【讨论】:

    【解决方案2】:

    好吧,忽略我之前的回答,我发现了实际问题。

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

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

    _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 读回文件的?
    • 需要明确的是,在执行我的示例代码后,我使用 AWS CLI 将文件复制到我的本地目录以查看内容。如果您通过浏览器检索 CSV,则默认情况下它可能采用与 utf-8 不同的编码。如果您按照我的方法(直接下载原始文件)并且内容正确,则只需找到适合您的浏览器的正确编码。
    • 那么我的示例和您的代码之间唯一可能的区别是您使用 csv.writerow() 编写的数据。我知道您的问题的全部意义在于避免使用硬盘,但是您尝试将写入硬盘作为调试解决方案?
    • 我花了很长时间才找到这个,但它解决了我的问题。谢谢!!
    【解决方案3】:

    您可以使用 goofys 之类的东西将输出重定向到 S3。

    【讨论】:

    • 谢谢@khc,但我试图使用我已经投资的东西,而不是使用新的库:)
    【解决方案4】:

    你试过先调用 buff.flush() 吗?您的完全合理的调试检查(调用 getvalue())可能会造成 buff 已被写入的错觉,但如果您不调用它,则不会。

    【讨论】:

    • 我试过了,buff.flush()调用是在csv写入到S3上传之间,S3上的csv文件还是空的。
    猜你喜欢
    • 2019-02-21
    • 2023-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-04
    • 2015-09-21
    • 2018-03-15
    相关资源
    最近更新 更多