S3.Client.upload_file()和S3.Client.upload_fileobj()有什么区别?

Fla*_*air 5 python amazon-s3 amazon-web-services python-3.x boto3

根据S3.Client.upload_fileS3.Client.upload_fileobjupload_fileobj听起来可能更快。但是有人知道细节吗?我应该只是上传文件,还是应该以二进制模式打开文件以使用upload_fileobj?换一种说法,

import boto3

s3 = boto3.resource('s3')

### Version 1
s3.meta.client.upload_file('/tmp/hello.txt', 'mybucket', 'hello.txt')

### Version 2
with open('/tmp/hello.txt', 'rb') as data:
    s3.upload_fileobj(data, 'mybucket', 'hello.txt')
Run Code Online (Sandbox Code Playgroud)

版本1或版本2更好吗?有区别吗?

Shm*_*ikA 8

TL; 博士

在速度方面,两种方法的性能大致相同,都是用python编写的,瓶颈是disk-io(从磁盘读取文件)或network-io(写入s3)。

  • upload_file()在编写仅处理从磁盘上传文件的代码时使用。
  • upload_fileobj()当您编写通用代码来处理 s3 上传时使用,这些代码将来不仅可以用于磁盘用例中的文件。


什么是 fileobj 呢?

在包括 python 标准库在内的多个地方都有约定,当人们使用术语fileobjshe 时表示类似文件的对象。甚至有些库公开了可以将文件路径 (str) 或 fileobj(类文件对象)作为相同参数的函数。

使用文件对象时,您的代码不仅限于磁盘,例如:

  1. 例如,您可以以流式方式将数据从一个 s3 对象复制到另一个对象中(不使用磁盘空间或减慢对磁盘进行读/写 io 的过程)。

  2. 将对象写入 S3 时,您可以(解)压缩或解密数据

以通用方式将 python gzip模块与类文件对象一起使用的示例:

import gzip, io

def gzip_greet_file(fileobj):
    """write gzipped hello message to a file"""
    with gzip.open(filename=fileobj, mode='wb') as fp:
        fp.write(b'hello!')

# using opened file
gzip_greet_file(open('/tmp/a.gz', 'wb'))

# using filename from disk
gzip_greet_file('/tmp/b.gz')

# using io buffer
file = io.BytesIO()
gzip_greet_file(file)
file.seek(0)
print(file.getvalue())
Run Code Online (Sandbox Code Playgroud)

另一方面,tarfile有两个参数 file & fileobj:

tarfile.open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs)
Run Code Online (Sandbox Code Playgroud)


即时压缩示例 s3.upload_fileobj()

import gzip, boto3

s3 = boto3.resource('s3')


def upload_file(fileobj, bucket, key, compress=False):
    if compress:
        fileobj = gzip.GzipFile(fileobj=fileobj, mode='rb')
        key = key + '.gz'
    s3.upload_fileobj(fileobj, bucket, key)
Run Code Online (Sandbox Code Playgroud)


Sam*_*uel 5

要点upload_fileobj是,文件对象不必首先存储在本地磁盘上,而可以表示为RAM中的文件对象。

Python 为此具有标准的库模块

代码看起来像

import io
fo = io.BytesIO(b'my data stored as file object in RAM')
s3.upload_fileobj(fo, 'mybucket', 'hello.txt')
Run Code Online (Sandbox Code Playgroud)

在这种情况下,由于您不必从本地磁盘读取数据,因此它将更快地执行。


jar*_*mod 2

两者都没有更好,因为它们没有可比性。虽然最终结果是相同的(一个对象上传到 S3),但它们获取该对象的方式却截然不同。一个期望您提供要上传的文件在磁盘上的路径,而另一个期望您提供一个类似文件的对象。

如果磁盘上有文件并想要上传它,请使用upload_file. 如果您有一个类似文件的对象(最终可能是很多东西,包括打开的文件、流、套接字、缓冲区、字符串),那么使用upload_fileobj.

在这种情况下,“类文件对象”是实现该read方法并返回字节的任何对象。