使用Python和AWS在Lambda中将文件写入S3

4
在AWS中,我正在尝试使用Lambda函数以Python的方式将文件保存到S3。虽然这在我的本地计算机上可以工作,但我无法让它在Lambda中工作。我已经花了大部分时间解决这个问题,希望能得到帮助。谢谢。
def pdfToTable(PDFfilename, apiKey, fileExt, bucket, key):

    # parsing a PDF using an API
    fileData = (PDFfilename, open(PDFfilename, "rb"))
    files = {"f": fileData}
    postUrl = "https://pdftables.com/api?key={0}&format={1}".format(apiKey, fileExt)
    response = requests.post(postUrl, files=files)
    response.raise_for_status()

    # this code is probably the problem!
    s3 = boto3.resource('s3')
    bucket = s3.Bucket('transportation.manifests.parsed')
    with open('/tmp/output2.csv', 'rb') as data:
        data.write(response.content)
        key = 'csv/' + key
        bucket.upload_fileobj(data, key)

    # FYI, on my own computer, this saves the file
    with open('output.csv', "wb") as f:
        f.write(response.content)

在S3中,有一个名为transportation.manifests.parsed的存储桶,其中包含应保存文件的文件夹csvresponse.content的类型是字节。
来自AWS的错误消息是[Errno 2] No such file or directory: '/tmp/output2.csv': FileNotFoundError. 实际上,我的目标是将文件保存到csv文件夹下,并使用唯一的名称,因此tmp/output2.csv可能不是最佳方法。有什么建议吗?
此外,我已尝试使用wbw而不是rb,但也没有成功。使用wb的错误是Input <_io.BufferedWriter name='/tmp/output2.csv'> of type: <class '_io.BufferedWriter'> is not supported. 文档建议使用'rb'是推荐的用法,但我不明白为什么会这样。
此外,我已尝试使用s3_client.put_object(Key=key, Body=response.content, Bucket=bucket),但收到调用HeadObject操作时出现错误(404):未找到的错误提示。

1
你有open('/tmp/output2.csv', 'rb'),但是你正在尝试写入文件。请注意,你可能不需要创建临时文件。Bucket 有一个put_object方法可以使用。 - Alasdair
刚刚更新了帖子以反映对您评论的回应。有什么想法? - tskittles
你需要解决@Alasdair指出的问题。你试图打开一个不存在的文件进行读取,因此你会得到FileNotFoundError。你需要将其打开以进行写入。另外,CSV文件是文本文件,而不是二进制文件,因此“wt”(或者只是“w”,因为文本是默认值)通常更合适。 - jarmod
@jarmod 当我将其切换为'w'时,错误是Input <_io.TextIOWrapper name='/tmp/output2.csv' mode='w' encoding='UTF-8'> of type: <class '_io.TextIOWrapper'> is not supported - tskittles
1
你需要使用'w'或者wb写入文件。你提供的文档是用于上传文件的,这是一个单独的步骤。你没有展示足够的信息来知道为什么put_object失败了。既然你已经有了bucket,我会这样做:bucket.put_object(Key=key, Body=response.content)。如果这不起作用,你应该展示你尝试过的完整代码和完整的traceback。 - Alasdair
@tskittles 我也遇到了同样的问题。这是我的 Stack Overflow 链接:https://stackoverflow.com/questions/68915908/save-image-data-from-a-iterator-object-to-aws-s3-in-python - kms
2个回答

6
假设您正在使用Python 3.6。我通常的做法是将字节内容包装在BytesIO包装器中,以创建类似文件的对象。根据boto3文档,您可以使用传输管理器进行托管传输:
from io import BytesIO
import boto3
s3 = boto3.client('s3')

fileobj = BytesIO(response.content)

s3.upload_fileobj(fileobj, 'mybucket', 'mykey')

如果这样做不起作用,我建议你仔细检查所有 IAM 权限是否正确。

我正在尝试将一个Avro文件写入S3。我正在使用Avro包中的DataFileWriter。请告诉我是否可以在不使用临时文件的情况下完成此操作。 - Minerva
抱歉,我不熟悉Avro。您可以将此作为新问题发布,我相信这样会得到更好的关注! - abigperson

4

您有一个可写流,正在要求boto3将其用作可读流,这是行不通的。

先写入文件,然后使用bucket.upload_file()函数,如下所示:

s3 = boto3.resource('s3')
bucket = s3.Bucket('transportation.manifests.parsed')
with open('/tmp/output2.csv', 'w') as data:
    data.write(response.content)

key = 'csv/' + key
bucket.upload_file('/tmp/output2.csv', key)

对于同时调用 Lambda 的情况,使用相同的 '/tmp/output2.csv' 不会创建冲突吗? - Alex_Y
1
@Alex_Y 不,同时运行的Lambda函数不会使用相同的运行环境。然而,在/tmp中可能会有来自先前Lambda函数调用的剩余文件,因此函数应该考虑到这一点(例如,删除或覆盖任何现有文件,或者仅创建一个唯一命名的文件)。 - jarmod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接