วิธีเขียนไฟล์หรือข้อมูลไปยังวัตถุ S3 โดยใช้ boto3


107

ใน boto 2 คุณสามารถเขียนไปยังวัตถุ S3 โดยใช้วิธีการเหล่านี้:

มี boto 3 เทียบเท่าหรือไม่? วิธี boto3 ในการบันทึกข้อมูลไปยังวัตถุที่จัดเก็บบน S3 คืออะไร?

คำตอบ:


214

ใน boto 3 เมธอด 'Key.set_contents_from_' ถูกแทนที่ด้วย

ตัวอย่างเช่น:

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')

หรืออีกวิธีหนึ่งข้อมูลไบนารีอาจมาจากการอ่านไฟล์ตามที่อธิบายไว้ในเอกสารทางการเปรียบเทียบ boto 2 และ 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: ไม่พบข้อมูลประจำตัวจะแก้ไขได้อย่างไร?
deepak murthy

2
@deepakmurthy ฉันไม่แน่ใจว่าทำไมคุณถึงได้รับข้อผิดพลาดนั้น ... คุณต้องถามคำถาม Stack Overflow ใหม่และให้รายละเอียดเพิ่มเติมเกี่ยวกับปัญหานี้
jkdev

1
เมื่อฉันพยายามที่ฉันจบลงด้วยวัตถุที่มีศูนย์s3.Object().put() content-lengthสำหรับฉันput()ยอมรับเฉพาะข้อมูลสตริง แต่put(str(binarydata)) ดูเหมือนว่าจะมีปัญหาในการเข้ารหัส ฉันจบลงด้วยวัตถุที่มีขนาดประมาณ 3 เท่าของข้อมูลต้นฉบับซึ่งทำให้ไม่มีประโยชน์สำหรับฉัน
user1129682

@ user1129682 ฉันไม่แน่ใจว่าทำไมถึงเป็นเช่นนั้น คุณช่วยถามคำถามใหม่และให้รายละเอียดเพิ่มเติมได้ไหม
jkdev

@jkdev มันต้องการจะดีหากคุณสามารถใช้เวลาดู
user1129682

51

boto3 ยังมีวิธีการอัพโหลดไฟล์โดยตรง:

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


5
เป็นสิ่งที่ดี แต่ไม่อนุญาตให้จัดเก็บข้อมูลในหน่วยความจำในปัจจุบัน
Reid

3
@Reid: สำหรับไฟล์ในหน่วยความจำคุณสามารถใช้s3.Bucket(...).upload_fileobj()วิธีนี้แทนได้
svohara

37

คุณไม่จำเป็นต้องแปลงเนื้อหาเป็นไบนารีอีกต่อไปก่อนที่จะเขียนลงไฟล์ใน S3 ตัวอย่างต่อไปนี้สร้างไฟล์ข้อความใหม่ (เรียกว่า newfile.txt) ในที่เก็บข้อมูล S3 ที่มีเนื้อหาสตริง:

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)

ไม่รู้ว่าการกระทำ "วาง" ของฉันไม่มีสิทธิ์เข้าถึง ฉันสร้างที่เก็บข้อมูลนี้และใส่รหัสมาตรฐานของฉันไว้ใต้รายการเข้าถึง
Chen Lin

คุณจะให้อย่างไรprefixในกรณีนี้? ความหมายถ้าคุณต้องการเก็บไฟล์ไว้my-bucket-name/subfolder/ล่ะ?
kev

3
@kev คุณสามารถระบุพร้อมกับชื่อไฟล์ "โฟลเดอร์ย่อย / newfile.txt" แทน "newfile.txt"
Madhava Carrillo

Re "คุณไม่จำเป็นต้องแปลงเนื้อหาเป็นไบนารีอีกต่อไปก่อนที่จะเขียนลงไฟล์ใน S3" เอกสารนี้มีเอกสารอยู่ที่ไหนบ้าง? ฉันกำลังดูboto3.amazonaws.com/v1/documentation/api/latest/reference/…และคิดว่ามันยอมรับเฉพาะไบต์เท่านั้น ฉันไม่แน่ใจว่าอะไรคือ "อ็อบเจ็กต์ที่มีลักษณะคล้ายไฟล์ที่สามารถค้นหาได้" แต่ไม่คิดว่าจะรวมสตริงไว้ด้วย
Emma

ฉันอาจเปรียบเทียบสิ่งนี้กับ download_fileobj () ซึ่งใช้สำหรับการอัปโหลดไฟล์หลายส่วนขนาดใหญ่ วิธีการอัปโหลดต้องการอ็อบเจ็กต์ไฟล์ที่สามารถค้นหาได้ แต่ put () ช่วยให้คุณสามารถเขียนสตริงลงในไฟล์ในที่เก็บข้อมูลได้โดยตรงซึ่งสะดวกสำหรับฟังก์ชันแลมบ์ดาในการสร้างและเขียนไฟล์ไปยังที่เก็บ S3 แบบไดนามิก
Franke

28

นี่เป็นเคล็ดลับที่ดีในการอ่าน JSON จาก s3:

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))

ตอนนี้คุณสามารถใช้json.load_s3และjson.dump_s3กับ API เช่นเดียวกับloadและdump

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

2
ยอดเยี่ยม. ...["Body"].read().decode('utf-8')ที่จะได้ไปทำงานผมเพิ่มบิตพิเศษนี้:
sedeh

ความคิดที่ดี. อย่างไรก็ตามมันมีพื้นที่สำหรับการปรับปรุงการตั้งชื่อ
ม.ค. Vlcinsky

เสนอความคิดที่ดีนี้เขียนใหม่: gist.github.com/vlcinsky/bbeda4321208aa98745afc29b58e90ac
Jan Vlcinsky

14

เวอร์ชันที่สะอาดและกระชับซึ่งฉันใช้เพื่ออัปโหลดไฟล์ได้ทันทีไปยังที่เก็บข้อมูล 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_idและaws_secret_access_key) ไว้ในไฟล์แยกต่างหากเสมอเช่น -~/.aws/credentials


ตำแหน่งที่เทียบเท่า Windows สำหรับไฟล์ข้อมูลรับรอง AWS คืออะไรเนื่องจาก Windows ไม่รองรับ~
Hamman Samuel

1
@ HammanSamuel คุณอาจเก็บไว้เช่นC:\Users\username\.aws\credentials
kev

1

ควรค่าแก่การกล่าวถึงสมาร์ทโอเพ่นที่ใช้boto3เป็นแบ็คเอนด์

smart-openเป็นดรอปแทนสำหรับหลามopenที่สามารถเปิดไฟล์จากs3เช่นเดียวกับftp, httpและโปรโตคอลอื่น ๆ อีกมากมาย

ตัวอย่างเช่น

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

ข้อมูลประจำตัว aws ถูกโหลดผ่านข้อมูลประจำตัว boto3โดยปกติจะเป็นไฟล์ใน~/.aws/dir หรือตัวแปรสภาพแวดล้อม


1
แม้ว่าการตอบกลับนี้จะให้ข้อมูล แต่ก็ไม่ได้เป็นไปตามการตอบคำถามเดิม - ซึ่งก็คือ boto3 ที่เทียบเท่ากับวิธีการ boto บางอย่างคืออะไร
robinhood91

1
Smart open ใช้ boto3
Uri Goren

1

คุณสามารถใช้โค้ดด้านล่างในการเขียนตัวอย่างเช่นรูปภาพไปยัง S3 ในปี 2019 เพื่อให้สามารถเชื่อมต่อกับ S3 ได้คุณจะต้องติดตั้ง AWS CLI โดยใช้คำสั่งpip install awscliจากนั้นป้อนข้อมูลรับรองโดยใช้คำสั่ง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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.