如何使用 boto3 将文件或数据写入 S3 对象

Posted

技术标签:

【中文标题】如何使用 boto3 将文件或数据写入 S3 对象【英文标题】:How to write a file or data to an S3 object using boto3 【发布时间】:2017-03-13 05:24:50 【问题描述】:

在 boto 2 中,您可以使用以下方法写入 S3 对象:

Key.set_contents_from_string() Key.set_contents_from_file() Key.set_contents_from_filename() Key.set_contents_from_stream()

是否有等效的 boto 3?将数据保存到存储在 S3 上的对象的 boto3 方法是什么?

【问题讨论】:

【参考方案1】:

在 boto 3 中,'Key.set_contents_from_' 方法被替换为

Object.put()

Client.put_object()

例如:

import boto3

some_binary_data = b'Here we have some data'
more_binary_data = b'Here we have some more data'

# Method 1: Object.put()
s3 = boto3.resource('s3')
object = s3.Object('my_bucket_name', 'my/key/including/filename.txt')
object.put(Body=some_binary_data)

# Method 2: Client.put_object()
client = boto3.client('s3')
client.put_object(Body=more_binary_data, Bucket='my_bucket_name', Key='my/key/including/anotherfilename.txt')

或者,二进制数据可以来自读取文件,如the official docs comparing boto 2 and boto 3中所述:

存储数据

从文件、流或字符串中存储数据很容易:

# Boto 2.x
from boto.s3.key import Key
key = Key('hello.txt')
key.set_contents_from_file('/tmp/hello.txt')

# Boto 3
s3.Object('mybucket', 'hello.txt').put(Body=open('/tmp/hello.txt', 'rb'))

【讨论】:

botocore.exceptions.NoCredentialsError: Unable to locate credentials 如何解决这个问题? @deepakmurthy 我不确定您为什么会收到该错误...您需要ask a new Stack Overflow question 并提供有关该问题的更多详细信息。 当我尝试s3.Object().put() 时,我最终得到一个content-length 为零的对象。对我来说put() 只接受字符串数据,但put(str(binarydata)) 似乎有某种编码问题。我最终得到一个大约是原始数据大小 3 倍的对象,这对我来说毫无用处。 @user1129682 我不确定为什么会这样。能否请ask a new question 提供更多详细信息? @jkdev 如果你能take a look,那就太好了。【参考方案2】:

boto3也有直接上传文件的方法:

s3 = boto3.resource('s3')    
s3.Bucket('bucketname').upload_file('/local/file/here.txt','folder/sub/path/to/s3key')

http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Bucket.upload_file

【讨论】:

这很好,但它不允许存储当前在内存中的数据。 @Reid:对于内存文件,您可以使用s3.Bucket(...).upload_fileobj() 方法。 从内存写入与从本地写入的文件上传到 s3 的性能如何?【参考方案3】:

在 S3 中写入文件之前,您不再需要将内容转换为二进制文件。以下示例在 S3 存储桶中创建一个包含字符串内容的新文本文件(称为 newfile.txt):

import boto3

s3 = boto3.resource(
    's3',
    region_name='us-east-1',
    aws_access_key_id=KEY_ID,
    aws_secret_access_key=ACCESS_KEY
)
content="String content to write to a new S3 file"
s3.Object('my-bucket-name', 'newfile.txt').put(Body=content)

【讨论】:

不知道我的“放置”操作无权访问。我创建了这个存储桶并将我的规范 ID 放在访问列表下。 在这种情况下你如何给prefix?意思是,如果您想将文件存储在my-bucket-name/subfolder/ 中怎么办? @kev 您可以指定它以及文件名 'subfolder/newfile.txt' 而不是 'newfile.txt' 关于“在 S3 中写入文件之前,您不再需要将内容转换为二进制文件。”,这是否记录在某处?我在看boto3.amazonaws.com/v1/documentation/api/latest/reference/…,并认为它只接受字节。我不确定究竟是什么构成了“可搜索的类文件对象”,但不认为包含字符串。 我可能会将其与用于大型多部分文件上传的 download_fileobj() 进行比较。上传方法需要seekable file objects,但 put() 允许您将字符串直接写入存储桶中的文件,这对于 lambda 函数可以方便地动态创建文件并将其写入 S3 存储桶。【参考方案4】:

这是一个从 s3 读取 JSON 的好技巧:

import json, boto3
s3 = boto3.resource("s3").Bucket("bucket")
json.load_s3 = lambda f: json.load(s3.Object(key=f).get()["Body"])
json.dump_s3 = lambda obj, f: s3.Object(key=f).put(Body=json.dumps(obj))

现在您可以使用与loaddump 相同的API 使用json.load_s3json.dump_s3

data = "test":0
json.dump_s3(data, "key") # saves json to s3://bucket/key
data = json.load_s3("key") # read json from s3://bucket/key

【讨论】:

优秀。为了让它工作,我添加了这个额外的位:...["Body"].read().decode('utf-8') 好主意。无论如何,它为命名改进提供了一些空间。 提议重写这个好主意:gist.github.com/vlcinsky/bbeda4321208aa98745afc29b58e90ac【参考方案5】:

一个更简洁的版本,我用来将文件即时上传到给定的 S3 存储桶和子文件夹-

import boto3

BUCKET_NAME = 'sample_bucket_name'
PREFIX = 'sub-folder/'

s3 = boto3.resource('s3')

# Creating an empty file called "_DONE" and putting it in the S3 bucket
s3.Object(BUCKET_NAME, PREFIX + '_DONE').put(Body="")

注意:您应该始终将您的 AWS 凭证(aws_access_key_idaws_secret_access_key)放在单独的文件中,例如 - ~/.aws/credentials

【讨论】:

AWS 凭证文件的 Windows 等效位置是什么,因为 Windows 不支持 ~ @HammanSamuel 你可以像C:\Users\username\.aws\credentials一样存储它 最好存放在 lambda 的环境变量中。【参考方案6】:

值得一提的是smart-open 使用boto3 作为后端。

smart-open 是 python 的 open 的替代品,可以从 s3 以及 ftphttp 和许多其他协议打开文件。

例如

from smart_open import open
import json
with open("s3://your_bucket/your_key.json", 'r') as f:
    data = json.load(f)

aws 凭据通过boto3 credentials 加载,通常是~/.aws/ 目录中的文件或环境变量。

【讨论】:

虽然此回复提供了丰富的信息,但它并未坚持回答原始问题 - 即某些 boto 方法的 boto3 等价物是什么。 智能开启使用boto3 @UriGoren 你能分享一个使用智能打开的 ftp 到 s3 的例子吗?【参考方案7】:

您可以使用以下代码编写,例如 2019 年的 S3 映像。为了能够连接到 S3,您必须使用命令 pip install awscli 安装 AWS CLI,然后使用命令 aws configure 输入一些凭据:

import urllib3
import uuid
from pathlib import Path
from io import BytesIO
from errors import custom_exceptions as cex

BUCKET_NAME = "xxx.yyy.zzz"
POSTERS_BASE_PATH = "assets/wallcontent"
CLOUDFRONT_BASE_URL = "https://xxx.cloudfront.net/"


class S3(object):
    def __init__(self):
        self.client = boto3.client('s3')
        self.bucket_name = BUCKET_NAME
        self.posters_base_path = POSTERS_BASE_PATH

    def __download_image(self, url):
        manager = urllib3.PoolManager()
        try:
            res = manager.request('GET', url)
        except Exception:
            print("Could not download the image from URL: ", url)
            raise cex.ImageDownloadFailed
        return BytesIO(res.data)  # any file-like object that implements read()

    def upload_image(self, url):
        try:
            image_file = self.__download_image(url)
        except cex.ImageDownloadFailed:
            raise cex.ImageUploadFailed

        extension = Path(url).suffix
        id = uuid.uuid1().hex + extension
        final_path = self.posters_base_path + "/" + id
        try:
            self.client.upload_fileobj(image_file,
                                       self.bucket_name,
                                       final_path
                                       )
        except Exception:
            print("Image Upload Error for URL: ", url)
            raise cex.ImageUploadFailed

        return CLOUDFRONT_BASE_URL + id

【讨论】:

以上是关于如何使用 boto3 将文件或数据写入 S3 对象的主要内容,如果未能解决你的问题,请参考以下文章

如何将文件上传到 S3 并使用 boto3 将其公开?

使用boto3,从整个文件夹或文件从一个s3存储桶复制到同一区域的另一个文件夹时,如何提供访问密钥和秘密访问密钥?

您如何使用 boto3(或其他方式)在 emr 上自动化 pyspark 作业?

Boto3 S3 更新现有对象的元数据

如何使用 Boto3 get_query_results 方法从 AWS Athena 创建数据框

将 Dataframe 保存到 csv 直接保存到 s3 Python