2011-10-02 30 views
13

Tôi đang sao chép tệp từ S3 sang Cloudfiles và tôi muốn tránh ghi tệp vào đĩa. Thư viện Python-Cloudfiles có một cuộc gọi object.stream() trông giống như những gì tôi cần, nhưng tôi không thể tìm thấy một cuộc gọi tương đương trong boto. Tôi hy vọng rằng tôi có thể làm một việc gì đó như:Làm thế nào tôi có thể sử dụng boto để truyền tệp ra khỏi Amazon S3 tới Cloudspace của Rackspace?

shutil.copyfileobj(s3Object.stream(),rsObject.stream()) 

Điều này có thể với boto (hoặc tôi giả sử bất kỳ thư viện s3 nào khác) không?

+0

Các [smart_open] (https://github.com/piskvorky/smart_open) thư viện Python làm điều đó (cả đọc và viết). – Radim

Trả lời

17

Đối tượng chính trong boto, đại diện cho trên đối tượng trong S3, có thể được sử dụng như một iterator, do đó bạn sẽ có thể làm điều gì đó như thế này:

>>> import boto 
>>> c = boto.connect_s3() 
>>> bucket = c.lookup('garnaat_pub') 
>>> key = bucket.lookup('Scan1.jpg') 
>>> for bytes in key: 
... write bytes to output stream 

Hoặc, như trong trường hợp ví dụ của bạn , bạn có thể làm:

>>> shutil.copyfileobj(key, rsObject.stream()) 
+0

một thư viện được thiết kế tốt như vậy :) – ehacinom

18

tôi tìm ít nhất một số người nhìn thấy câu hỏi này sẽ được như tôi, và sẽ muốn có một cách để truyền một tập tin từ dòng boto bởi dòng (hoặc dấu phẩy bằng dấu phẩy, hoặc bất kỳ khác delimiter). Dưới đây là một cách đơn giản để làm điều đó:

def getS3ResultsAsIterator(self, aws_access_info, key, prefix):   
    s3_conn = S3Connection(**aws_access) 
    bucket_obj = s3_conn.get_bucket(key) 
    # go through the list of files in the key 
    for f in bucket_obj.list(prefix=prefix): 
     unfinished_line = '' 
     for byte in f: 
      byte = unfinished_line + byte 
      #split on whatever, or use a regex with re.split() 
      lines = byte.split('\n') 
      unfinished_line = lines.pop() 
      for line in lines: 
       yield line 

@ câu trả lời của garnaat ở trên vẫn tuyệt vời và đúng 100%. Hy vọng rằng tôi vẫn giúp ai đó ra ngoài.

+0

chia thành hai dòng kết thúc khác với: 'lines = re.split (r '[\ n \ r] +', byte)' - hữu ích cho các tệp CSV được xuất từ ​​Excel – marcfrodi

+2

một lưu ý: Tôi đã phải thêm 'yield unfinished_line' sau khi vòng lặp' for byte in f: 'đã hoàn tất, nếu không dòng cuối cùng sẽ không được xử lý – marcfrodi

+1

Có lý do chính đáng tại sao đây không phải là một phần của API Boto3 không? Nếu không, có nên gửi yêu cầu kéo để sửa lỗi này không? Tôi sẽ siêu xuống vì gõ một cái gì đó như nó lên! – lol

13

Các câu trả lời khác trong chuỗi này có liên quan đến boto, nhưng S3.Object không thể lặp lại được nữa trong boto3. Vì vậy, sau đây không hoạt động, nó tạo ra một thông báo TypeError: 's3.Object' object is not iterable lỗi:

s3 = boto3.session.Session(profile_name=my_profile).resource('s3') 
    s3_obj = s3.Object(bucket_name=my_bucket, key=my_key) 

    with io.FileIO('sample.txt', 'w') as file: 
     for i in s3_obj: 
      file.write(i) 

Trong boto3, nội dung của các đối tượng có sẵn tại S3.Object.get()['Body'] mà không phải là một iterable một trong hai, vì vậy sau vẫn không hoạt động:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     for i in body: 
      file.write(i) 

Vì vậy, một sự thay thế là sử dụng phương pháp đọc, nhưng điều này nạp các đối tượng S3 WHOLE trong bộ nhớ mà khi giao dịch với các tập tin lớn không phải lúc nào một khả năng:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     for i in body.read(): 
      file.write(i) 

Nhưng phương pháp read cho phép truyền vào tham số amt chỉ định số byte mà chúng tôi muốn đọc từ luồng cơ bản. Phương pháp này có thể được lặp đi lặp lại gọi cho đến khi toàn bộ dòng đã được đọc:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     while file.write(body.read(amt=512)): 
      pass 

Đào vào botocore.response.StreamingBody một mã nhận ra rằng dòng cơ bản cũng có sẵn, vì vậy chúng tôi có thể lặp như sau:

body = s3_obj.get()['Body'] 
    with io.FileIO('sample.txt', 'w') as file: 
     for b in body._raw_stream: 
      file.write(b) 

Trong khi googling tôi cũng nhìn thấy một số liên kết có thể được sử dụng, nhưng tôi đã không cố gắng:

+1

Câu trả lời rất hữu ích. Cảm ơn @smallo. Tôi đánh giá cao rằng bạn đã tiếp xúc với __raw_stream riêng tư đó là những gì tôi nghĩ hầu hết mọi người đang tìm kiếm. – saccharine

1

Đây là giải pháp của tôi về gói luồng cơ thể:

import io 
class S3ObjectInterator(io.RawIOBase): 
    def __init__(self, bucket, key): 
     """Initialize with S3 bucket and key names""" 
     self.s3c = boto3.client('s3') 
     self.obj_stream = self.s3c.get_object(Bucket=bucket, Key=key)['Body'] 

    def read(self, n=-1): 
     """Read from the stream""" 
     return self.obj_stream.read() if n == -1 else self.obj_stream.read(n) 

Ví dụ sử dụng:

obj_stream = S3ObjectInterator(bucket, key) 
for line in obj_stream: 
    print line 
Các vấn đề liên quan