s3.upload_fileobj给出错误,需要一个类似字节的对象

时间:2019-06-19 07:49:34

标签: python pandas amazon-s3

我的问题是受previous SO的启发而生的,该主题涉及:在Amazon Web Services(AWS)S3中将DataFrames作为csv文件上传和保存。使用Python3,我想使用s3.upload_fileobj(分段上传)来使数据更快地传输到S3。当我在接受的答案中运行代码时,收到一条错误消息:“ TypeError:需要一个类似字节的对象,而不是'str'”。

答案最近已被多次拒绝。因此,我认为必须有一种在Python3中运行该代码而不会出错的方法。

请在代码下方找到。让我们轻松使用一个简单的DataFrame。实际上,此DataFrame更大(大约500 MB)。

import pandas as pd
import io

df = pd.DataFrame({'A':[1,2,3], 'B':[6,7,8]})

代码如下。为了方便起见,我打开了它:

def upload_file(dataframe, bucket, key):
    """dat=DataFrame, bucket=bucket name in AWS S3, key=key name in AWS S3"""
    s3 = boto3.client('s3')
    csv_buffer = io.BytesIO()
    dataframe.to_csv(csv_buffer, compression='gzip')
    s3.upload_fileobj(csv_buffer, bucket, key)

upload_file(df, your-bucket, your-key)

非常感谢您的建议!

3 个答案:

答案 0 :(得分:1)

this reference开始,看来您需要在gzip.GzipFile周围包裹一个BytesIO对象,然后才能为您执行压缩。

import io
import gzip

buffer = io.BytesIO()     
with gzip.GzipFile(fileobj=buffer, mode="wb") as f:
    f.write(df.to_csv().encode())

s3.upload_fileobj(buffer, bucket, key)

最小可验证示例

import io
import gzip
import zlib

# Encode
df = pd.DataFrame({'A':[1,2,3], 'B':[6,7,8]})

buffer = io.BytesIO()     
with gzip.GzipFile(fileobj=buffer, mode="wb") as f:
    f.write(df.to_csv().encode())

buffer.getvalue()
# b'\x1f\x8b\x08\x00\xf0\x0b\x11]\x02\xff\xd3q\xd4q\xe22\xd01\xd41\xe32\xd41\xd21\xe72\xd21\xd6\xb1\xe0\x02\x00Td\xc2\xf5\x17\x00\x00\x00'

# Decode
print(zlib.decompress(out.getvalue(), 16+zlib.MAX_WBITS).decode())

# ,A,B
# 0,1,6
# 1,2,7
# 2,3,8

答案 1 :(得分:1)

您唯一需要的是TextIOWrapper,因为to_csv期望string,而upload_fileobj期望bytes

def upload_file(dataframe, bucket, key):
    """dat=DataFrame, bucket=bucket name in AWS S3, key=key name in AWS S3"""
    s3 = boto3.client('s3')
    csv_buffer = io.BytesIO()
    w = io.TextIOWrapper(csv_buffer)
    dataframe.to_csv(w, compression='gzip')
    w.seek(0)
    s3.upload_fileobj(csv_buffer, bucket, key)

代码上传正常

$ cat test.csv
,A,B
0,1,6
1,2,7
2,3,8

答案 2 :(得分:-2)

您可以尝试类似的方法。

import pandas as pd
import io

df = pd.DataFrame({'A':[1,2,3], 'B':[6,7,8]})

def upload_file(dataframe, bucket, key):
    """dat=DataFrame, bucket=bucket name in AWS S3, key=key name in AWS S3"""
    s3 = boto3.client('s3')
    csv_buffer = io.StringIO()
    dataframe.to_csv(csv_buffer, compression='gzip')
    csv_buffer.seek(0)
    s3.upload_fileobj(csv_buffer, bucket, key)

upload_file(df, your-bucket, your-key)