Boto3 中的 IOError download_file

IOError in Boto3 download_file

背景

我正在使用以下 Boto3 代码从 S3 下载文件。

for record in event['Records']:
    bucket = record['s3']['bucket']['name']
    key = record['s3']['object']['key']
    print (key)
    if key.find('/') < 0 :
    if len(key) > 4 and key[-5:].lower() == '.json': //File is uploaded outside any folder

        download_path = '/tmp/{}{}'.format(uuid.uuid4(), key)
    else:
        download_path = '/tmp/{}/{}'.format(uuid.uuid4(), key)//File is uploaded inside a folder

如果在 S3 存储桶中上传新文件,将触发此代码,并通过此代码下载新上传的文件。

当上传到任何文件夹之外时,此代码工作正常。

但是,当我在目录中上传文件时,发生 IO 错误。 这是我遇到的 IO 错误的转储。

[Errno 2] No such file or directory: /tmp/316bbe85-fa21-463b-b965-9c12b0327f5d/test1/customer1.json.586ea9b8: IOError

test1 是我的 S3 存储桶中上传 customer1.json 的目录。

查询

关于如何解决这个错误有什么想法吗?

出现错误是因为您试图将文件下载并保存到不存在的目录中。使用os.mkdir之前下载文件创建目录。

# ...
else:
    item_uuid = str(uuid.uuid4())
    os.mkdir('/tmp/{}'.format(item_uuid))
    download_path = '/tmp/{}/{}'.format(item_uuid, key)  # File is uploaded inside a folder

注意:使用系统路径操作时最好使用os.path.join()。所以上面的代码可以重写为:

# ...
else:
    item_uuid = str(uuid.uuid4())
    os.mkdir(os.path.join(['tmp', item_uuid]))
    download_path = os.path.join(['tmp', item_uuid, key]))

也可能会引发错误,因为您在 s3 存储桶文件的下载路径中包含“/tmp/”,但不包含 tmp 文件夹,因为它可能在 s3 上不存在。使用这些文章确保您走在正确的道路上:

感谢 Andriy Ivaneyko 的帮助,我找到了使用 boto3 的解决方案。

使用下面的代码我能够完成我的任务。

for record in event['Records']:
    bucket = record['s3']['bucket']['name']
    key = record['s3']['object']['key']
    fn='/tmp/xyz'
    fp=open(fn,'w')
    response = s3_client.get_object(Bucket=bucket,Key=key)
    contents = response['Body'].read()
    fp.write(contents)
    fp.close()

我遇到了同样的问题,错误消息引起了很多混乱,(文件名后的随机字符串扩展名)。在我的例子中,它是由丢失的目录路径引起的,该路径不存在。

您的代码的问题在于 download_path 是错误的。每当您尝试下载 s3 存储桶中目录下的任何文件时,下载路径将变为:

download_path = /tmp/<uuid><object key name>
where <object key name>  = "<directory name>/<object name>"

这使得下载路径为:

download_path = /tmp/<uuid><directory name>/<object key name>

代码将失败,因为不存在具有 uuid 目录名称的目录。您的代码只允许下载 /tmp 目录下的文件。

要解决此问题,考虑在创建下载路径时拆分您的密钥,您也可以避免检查文件上传到存储桶中的位置。这只会在下载路径中获取目标文件名。例如:

for record in event['Records']:
    bucket = record['s3']['bucket']['name']
    key = record['s3']['object']['key']
    print (key) 
    download_path = '/tmp/{}{}'.format(uuid.uuid4(), key.split('/')[-1])