使用 boto3 和回调跟踪 S3 文件的下载进度

新手上路,请多包涵

我正在尝试使用 boto3 从 S3 下载文本文件。

这是我写的。

 class ProgressPercentage(object):
    def __init__(self, filename):
        self._filename = filename
        self._size = float(os.path.getsize(filename))
        self._seen_so_far = 0
        self._lock = threading.Lock()

    def __call__(self, bytes_amount):
        # To simplify we'll assume this is hooked up
        # to a single filename.
        with self._lock:
            self._seen_so_far += bytes_amount
            percentage = round((self._seen_so_far / self._size) * 100,2)
            LoggingFile('{} is the file name. {} out of {} done. The percentage completed is {} %'.format(str(self._filename), str(self._seen_so_far), str(self._size),str(percentage)))
            sys.stdout.flush()

我用它来称呼它

transfer.download_file(BUCKET_NAME,FILE_NAME,'{}{}'.format(LOCAL_PATH_TEMP , FILE_NAME),callback = ProgressPercentage(LOCAL_PATH_TEMP + FILE_NAME))

这给我一个错误,文件不存在于文件夹中。显然,当我在同一个文件夹中已经有一个同名的文件时,它可以工作,但是当我下载一个新文件时,它会出错。

我需要做哪些更正?

原文由 Kshitij Marwah 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

callback = ProgressPercentage(LOCAL_PATH_TEMP + FILE_NAME)) creates a ProgressPercentage object, runs its __init__ method, and passes the object as callback to the download_file 方法。这意味着 __init__ 方法在 download_file 开始 之前 运行。

__init__ 方法中,您试图读取正在下载到的本地文件的大小,这会引发异常,因为该文件不存在,因为下载尚未开始。如果您已经下载了该文件,那么就没有问题,因为存在本地副本并且可以读取其大小。

当然,这只是您所看到的异常的原因。您正在使用 _size 属性作为下载进度的最大值。但是,您正在尝试使用本地文件的大小。在文件下载完成之前,本地文件系统不知道文件有多大,它只知道现在占用了多少空间。这意味着当您下载文件时,文件会逐渐变大,直到达到其完整大小。因此,将本地文件的大小视为下载的最大大小并没有多大意义。它可能适用于您已经下载文件的情况,但这不是很有用。

您的问题的解决方案是检查您要下载的文件的大小,而不是检查本地副本的大小。这可确保您获得所下载内容的实际大小,并且该文件存在(因为如果不存在则无法下载)。您可以通过使用 head_object 获取远程文件的大小来执行此操作,如下所示

class ProgressPercentage(object):
    def __init__(self, client, bucket, filename):
        # ... everything else the same
        self._size = client.head_object(Bucket=bucket, Key=filename).ContentLength

    # ...

# If you still have the client object you could pass that directly
# instead of transfer._manager._client
progress = ProgressPercentage(transfer._manager._client, BUCKET_NAME, FILE_NAME)
transfer.download_file(..., callback=progress)

最后一点,虽然您从 Boto3 文档 中获得了代码,但它不起作用,因为它是用于文件上传的。在这种情况下,本地文件是源文件并且它的存在得到保证。

原文由 yummies 发布,翻译遵循 CC BY-SA 3.0 许可协议

这是我的实现。没有其他依赖项,破解进度回调函数以显示您想要的任何内容。

 import sys
import boto3

s3_client = boto3.client('s3')

def download(local_file_name, s3_bucket, s3_object_key):

    meta_data = s3_client.head_object(Bucket=s3_bucket, Key=s3_object_key)
    total_length = int(meta_data.get('ContentLength', 0))
    downloaded = 0

    def progress(chunk):
        nonlocal downloaded
        downloaded += chunk
        done = int(50 * downloaded / total_length)
        sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50-done)) )
        sys.stdout.flush()

    print(f'Downloading {s3_object_key}')
    with open(local_file_name, 'wb') as f:
        s3_client.download_fileobj(s3_bucket, s3_object_key, f, Callback=progress)

例如

local_file_name = 'test.csv'
s3_bucket = 'my-bucket'
s3_object_key = 'industry/test.csv'

download(local_file_name, s3_bucket, s3_object_key)

演示:

在此处输入图像描述

经测试 boto3>=1.14.19python>=3.7

原文由 Glen Thompson 发布,翻译遵循 CC BY-SA 4.0 许可协议

推荐问题