2013-04-02 14 views
7

Tôi có một tệp cục bộ lớn. Tôi muốn tải lên phiên bản gzipped của tệp đó vào S3 bằng cách sử dụng thư viện boto. Tệp quá lớn để gzip hiệu quả trên đĩa trước khi tải lên, vì vậy tệp phải được nén theo cách được truyền trực tuyến trong khi tải lên.Cách tải xuống gzip trong khi tải lên s3 bằng boto

Thư viện boto biết chức năng set_contents_from_file() dự kiến ​​một đối tượng giống như tệp sẽ đọc từ đó.

Thư viện gzip biết lớp học GzipFile có thể nhận đối tượng thông qua tham số có tên fileobj; nó sẽ ghi vào đối tượng này khi nén.

Tôi muốn kết hợp hai hàm này, nhưng một API muốn tự đọc, API kia muốn tự viết; không biết một hoạt động thụ động (như được viết hoặc đọc).

Có ai có ý tưởng về cách kết hợp chúng theo cách làm việc không?

EDIT: Tôi đã chấp nhận một câu trả lời (xem bên dưới) vì nó gợi ý tôi đi đâu, nhưng nếu bạn có cùng một vấn đề, bạn có thể tìm thấy câu trả lời của riêng tôi (cũng dưới đây) hữu ích hơn tải lên nhiều phần trong đó.

Trả lời

3

Có thực sự không phải là một cách để làm điều này bởi vì S3 không hỗ trợ đầu vào trực tuyến thực sự (tức là mã hóa chuyển chunked). Bạn phải biết Độ dài nội dung trước khi tải lên và cách duy nhất để biết rằng trước tiên phải thực hiện thao tác gzip.

+0

sẽ tải lên S3 thực sự cần phải biết kích thước của giá trị? Điều đó thực sự có nghĩa là không thể nén trực tuyến trong khi lưu trữ có thể được thực hiện. Tôi sẽ kiểm tra điều này. – Alfe

+0

Có một 'set_contents_from_stream()' trong boto-s3-bucket-keys. Điều đó ít nhất gợi ý về việc phát trực tuyến đó là có thể, bạn có nghĩ vậy không? – Alfe

+0

Từ tài liệu của nó: 'Các đối tượng dòng là không thể tìm kiếm và tổng kích thước không được biết đến. Điều này có ngụ ý rằng chúng tôi không thể chỉ định Kích thước nội dung và Nội dung-MD5 trong tiêu đề. Vì vậy, đối với các bản tải lên lớn , việc chậm trễ tính toán MD5 tránh được nhưng với một hình phạt không có khả năng xác minh tính toàn vẹn của dữ liệu đã tải lên .' – Alfe

18

tôi thực hiện các giải pháp ám chỉ trong các ý kiến ​​của câu trả lời chấp nhận bởi garnaat:

import cStringIO 
import gzip 

def sendFileGz(bucket, key, fileName, suffix='.gz'): 
    key += suffix 
    mpu = bucket.initiate_multipart_upload(key) 
    stream = cStringIO.StringIO() 
    compressor = gzip.GzipFile(fileobj=stream, mode='w') 

    def uploadPart(partCount=[0]): 
     partCount[0] += 1 
     stream.seek(0) 
     mpu.upload_part_from_file(stream, partCount[0]) 
     stream.seek(0) 
     stream.truncate() 

    with file(fileName) as inputFile: 
     while True: # until EOF 
      chunk = inputFile.read(8192) 
      if not chunk: # EOF? 
       compressor.close() 
       uploadPart() 
       mpu.complete_upload() 
       break 
      compressor.write(chunk) 
      if stream.tell() > 10<<20: # min size for multipart upload is 5242880 
       uploadPart() 

Có vẻ như để làm việc mà không vấn đề. Và sau tất cả, luồng trong hầu hết các trường hợp chỉ là một đoạn dữ liệu. Trong trường hợp này, các khối lớn khoảng 10MB, nhưng ai quan tâm? Miễn là chúng tôi không nói về một số khối GB, tôi tốt với điều này.


Cập nhật cho Python 3:

from io import BytesIO 
import gzip 

def sendFileGz(bucket, key, fileName, suffix='.gz'): 
    key += suffix 
    mpu = bucket.initiate_multipart_upload(key) 
    stream = BytesIO() 
    compressor = gzip.GzipFile(fileobj=stream, mode='w') 

    def uploadPart(partCount=[0]): 
     partCount[0] += 1 
     stream.seek(0) 
     mpu.upload_part_from_file(stream, partCount[0]) 
     stream.seek(0) 
     stream.truncate() 

    with open(fileName, "rb") as inputFile: 
     while True: # until EOF 
      chunk = inputFile.read(8192) 
      if not chunk: # EOF? 
       compressor.close() 
       uploadPart() 
       mpu.complete_upload() 
       break 
      compressor.write(chunk) 
      if stream.tell() > 10<<20: # min size for multipart upload is 5242880 
       uploadPart()