如何使用boto从S3下载文件,仅在远程文件比本地副本新时?

13 投票
1 回答
12315 浏览
提问于 2025-04-18 16:38

我正在尝试使用boto从S3下载一个文件,但只有在本地文件比远程文件旧的时候才下载。

我使用了一个叫做'If-Modified-Since'的头部信息,下面是我的代码:

#!/usr/bin/python
import os
import datetime
import boto
from boto.s3.key import Key

bucket_name = 'my-bucket'

conn = boto.connect_s3()
bucket = conn.get_bucket(bucket_name)

def download(bucket, filename):
    key = Key(bucket, filename)
    headers = {}
    if os.path.isfile(filename):
        print "File exists, adding If-Modified-Since header"
        modified_since = os.path.getmtime(filename)
        timestamp = datetime.datetime.utcfromtimestamp(modified_since)
        headers['If-Modified-Since'] = timestamp.strftime("%a, %d %b %Y %H:%M:%S GMT")
    try:
        key.get_contents_to_filename(filename, headers)
    except boto.exception.S3ResponseError as e:
        return 304
    return 200

print download(bucket, 'README')

问题是,当本地文件不存在时,一切都正常,文件会被下载。但是当我第二次运行这个脚本时,我的函数返回了304,正如预期的那样,但之前下载的文件却被删除了。

1 个回答

9

boto.s3.key.Key.get_contents_to_filename 这个函数是用来打开文件的,它使用的是 wb 模式,也就是以“写入二进制”的方式打开文件。在这个函数开始的时候,它会把文件内容清空,也就是说,文件原来的内容会被删除。此外,如果在执行过程中出现错误,这个函数还会把文件删除。

如果你不想这样,可以用 get_contents_to_file 这个函数,它可以用不同的方式打开文件。

def download(bucket, filename):
    key = Key(bucket, filename)
    headers = {}
    mode = 'wb'
    updating = False
    if os.path.isfile(filename):
        mode = 'r+b'
        updating = True
        print "File exists, adding If-Modified-Since header"
        modified_since = os.path.getmtime(filename)
        timestamp = datetime.datetime.utcfromtimestamp(modified_since)
        headers['If-Modified-Since'] = timestamp.strftime("%a, %d %b %Y %H:%M:%S GMT")
    try:
        with open(filename, mode) as f:
            key.get_contents_to_file(f, headers)
            f.truncate()
    except boto.exception.S3ResponseError as e:
        if not updating:
            # got an error and we are not updating an existing file
            # delete the file that was created due to mode = 'wb'
            os.remove(filename)
        return e.status
    return 200

注意 file.truncate 这个函数是用来处理新文件比旧文件小的情况的。

撰写回答