簡體   English   中英

什么是 Python 的 `open()` function 的替代品,用於在 S3 上讀/寫文件?

[英]What is a drop-in replacement for Python's `open()` function to read/write a file on S3?

在 AWS Lambda function 中使用 Amazon S3 存儲桶時,有什么好方法可以替代 Python 的內置open() function?

概括

  • 我正在尋找一種在 AWS Lambda function 中從 Amazon S3 下載文件或將文件上傳到 Amazon S3 的方法。
  • 語法/API 應該類似於 Python 的內置open() ,特別是返回一個類似於 object 的文件,它可以傳遞給其他函數,如pandas.read_csv()
    • 我最感興趣的是read()write()而不是seek()tell() ,例如PIL.Image.open()所需要的。
  • 該方法應使用 AWS Lambda 中已有的庫,例如 boto3。
  • 它應該使 Lambda 的部署規模保持較小。 因此不像s3fs這樣的大依賴項,這對於 AWS Lambda 通常是過大的。

這是我在想的一個例子。

filename = "s3://mybucket/path/to/file.txt"
outpath = "s3://mybucket/path/to/lowercase.txt"

with s3_open(filename) as fd, s3_open(outpath, "wt") as fout:
    for line in fd:
        fout.write(line.strip().lower())

動機

大多數使用 Python 的人都熟悉

filename = "/path/to/file.txt"
with open(filename) as fd:
    lines = fd.readlines()

使用 Amazon S3 的人可能也熟悉S3 URIs ,但是 S3 URIs 不方便與boto3一起使用,Amazon S3 Python SDK:

  • boto3 使用s3.get_object(Bucket=bucket, Key=key)之類的參數,而我通常使用 S3 URI
  • boto3 返回一個 json 響應,其中包含一個 StreamingBody 而我想要的只是 StreamingBody
  • StreamingBody 返回字節,但文本通常更方便

許多 Python 庫接受 類文件對象,例如 json、pandas、zipfile。

我通常只需要將單個文件下載/上傳到 S3,因此無需管理整個文件系統。 我也不需要或不想將文件保存到磁盤只是為了將其讀回 memory。

一個開始

import io
import boto3

session = boto3.Session()
s3_client = boto3.client("s3")

def s3uriparse(s3_uri):
    raise NotImplmementedError


def s3_open(s3_uri, mode="rt"):
    bucket, key = s3uriparse(s3_uri)
    
    if mode.startswith("r"):
        r = s3_client.get_object(Bucket=bucket, Key=key)
        fileobj = r["StreamingBody"]
        if mode.endswith("t"):
            fileobj = io.TextIOWrapper(fileobj._raw_stream)
        return fileobj
    elif mode.startswith("w"):
        # Write mode
        raise NotImplementedError
    else:
        raise ValueError("Invalid mode")

有一個 Python 庫,叫做smart-open·PyPI

它非常好,因為您可以使用您熟悉的所有文件處理命令,並且它適用於 S3 對象。 它還可以從壓縮文件中讀取。

>>> from smart_open import open
>>>
>>> # stream lines from an S3 object
>>> for line in open('s3://commoncrawl/robots.txt'):
...    print(repr(line))
...    break
'User-Agent: *\n'

>>> # stream from/to compressed files, with transparent (de)compression:
>>> for line in open('smart_open/tests/test_data/1984.txt.gz', encoding='utf-8'):
...    print(repr(line))
'It was a bright cold day in April, and the clocks were striking thirteen.\n'
'Winston Smith, his chin nuzzled into his breast in an effort to escape the vile\n'
'wind, slipped quickly through the glass doors of Victory Mansions, though not\n'
'quickly enough to prevent a swirl of gritty dust from entering along with him.\n'

>>> # can use context managers too:
>>> with open('smart_open/tests/test_data/1984.txt.gz') as fin:
...    with open('smart_open/tests/test_data/1984.txt.bz2', 'w') as fout:
...        for line in fin:
...           fout.write(line)
74
80
78
79

>>> # can use any IOBase operations, like seek
>>> with open('s3://commoncrawl/robots.txt', 'rb') as fin:
...     for line in fin:
...         print(repr(line.decode('utf-8')))
...         break
...     offset = fin.seek(0)  # seek to the beginning
...     print(fin.read(4))
'User-Agent: *\n'
b'User'

>>> # stream from HTTP
>>> for line in open('http://example.com/index.html'):
...     print(repr(line))
...     break
'<!doctype html>\n'

我對你的動機感到困惑:有什么問題

s3 = boto3.resource('s3')
s3_object = s3.Object(bucket_name, full_key)
s3_object.put(Body=byte_stream_of_some_kind)

寫和

s3 = boto3.client('s3')
s3_object_byte_stream = s3.get_object(Bucket=bucket_name, Key=object_key)['Body'].read()

對於stream中的object到你的lambda要更新嗎?

這兩個功能 stream 直接進入或離開 s3 - 你不必下載 object 然后在 stream 中打開它 - 如果你願意,你仍然可以使用 with 語句來自動關閉它們(在資源上使用它)

s3 中也沒有文件系統——雖然我們使用 object 鍵和類似命名法的文件系統('/' 並且在控制台中顯示為目錄,但 s3 內部的實際布局與名稱具有一定的解析能力。因此,如果您知道 object 的完整密鑰,您可以將其 stream 進出您的 lambda,而無需任何下載

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM