2015-06-24 17 views
17

Tôi cần phải tạo một CSV và tải nó lên một thùng S3. Vì tôi đang tạo tập tin trên bay, nó sẽ tốt hơn nếu tôi có thể viết nó trực tiếp vào thùng S3 vì nó đang được tạo chứ không phải viết toàn bộ tệp cục bộ và sau đó tải lên tệp ở cuối.Bạn có thể tải lên S3 bằng cách sử dụng luồng thay vì tệp cục bộ không?

Có cách nào để thực hiện việc này không? Dự án của tôi là bằng Python và tôi khá mới đối với ngôn ngữ. Đây là những gì tôi đã cố gắng cho đến nay:

import csv 
import csv 
import io 
import boto 
from boto.s3.key import Key 


conn = boto.connect_s3() 
bucket = conn.get_bucket('dev-vs') 
k = Key(bucket) 
k.key = 'foo/foobar' 

fieldnames = ['first_name', 'last_name'] 
writer = csv.DictWriter(io.StringIO(), fieldnames=fieldnames) 
k.set_contents_from_stream(writer.writeheader()) 

tôi nhận được lỗi này: BotoClientError: s3 không hỗ trợ chuyển chunked

UPDATE: Tôi tìm thấy một cách để viết trực tiếp lên S3, nhưng tôi không thể tìm một cách để xóa bộ đệm mà không thực sự xóa các dòng mà tôi đã viết. Vì vậy, ví dụ:

conn = boto.connect_s3() 
bucket = conn.get_bucket('dev-vs') 
k = Key(bucket) 
k.key = 'foo/foobar' 

testDict = [{ 
    "fieldA": "8", 
    "fieldB": None, 
    "fieldC": "888888888888"}, 
    { 
    "fieldA": "9", 
    "fieldB": None, 
    "fieldC": "99999999999"}] 

f = io.StringIO() 
fieldnames = ['fieldA', 'fieldB', 'fieldC'] 
writer = csv.DictWriter(f, fieldnames=fieldnames) 
writer.writeheader() 
k.set_contents_from_string(f.getvalue()) 

for row in testDict: 
    writer.writerow(row) 
    k.set_contents_from_string(f.getvalue()) 

f.close() 

Viết 3 dòng vào file, tuy nhiên tôi không thể giải phóng bộ nhớ để viết một tập tin lớn. Nếu tôi thêm:

f.seek(0) 
f.truncate(0) 

vào vòng lặp, thì chỉ dòng cuối cùng của tệp được ghi. Có cách nào để giải phóng tài nguyên mà không xóa các dòng khỏi tệp không?

+0

Thậm chí nếu bạn có thể viết thư cho S3 như bạn muốn, tôi sẽ không khuyên bạn nên nó do những thách thức nhất quán . Tại sao bạn nghĩ sẽ tốt hơn nếu không viết cục bộ? Bạn có muốn một đối tượng S3 một phần nếu có một ngoại lệ hoặc vấn đề? Tôi đoán là không. – cgseller

+1

Tôi đang tìm cách viết trực tiếp để có hiệu quả hơn một chút. Về cơ bản nếu tôi viết tệp cục bộ và tải tệp lên, tôi đang thêm tải lên dưới dạng bước bổ sung và làm sạch tệp cục bộ. Tôi không nhớ có một tập tin không đầy đủ - tôi có thể có một tập tin không đầy đủ nếu tôi đã viết nó cục bộ quá. Hệ thống sẽ không hoạt động và xóa một tệp ở trạng thái lỗi hoặc tiếp tục. –

Trả lời

16

Tôi đã tìm được giải pháp cho câu hỏi của mình, mà tôi sẽ đăng ở đây trong trường hợp bất kỳ ai khác quan tâm. Tôi quyết định làm điều này như một phần trong quá trình tải lên nhiều phần. Bạn không thể truyền tới S3. Ngoài ra còn có một gói có sẵn mà thay đổi tập tin trực tuyến của bạn qua một tải lên đa mà tôi đã sử dụng: Smart Open.

import smart_open 
import io 
import csv 

testDict = [{ 
    "fieldA": "8", 
    "fieldB": None, 
    "fieldC": "888888888888"}, 
    { 
    "fieldA": "9", 
    "fieldB": None, 
    "fieldC": "99999999999"}] 

fieldnames = ['fieldA', 'fieldB', 'fieldC'] 
f = io.StringIO() 
with smart_open.smart_open('s3://dev-test/bar/foo.csv', 'wb') as fout: 
    writer = csv.DictWriter(f, fieldnames=fieldnames) 
    writer.writeheader() 
    fout.write(f.getvalue()) 

    for row in testDict: 
     f.seek(0) 
     f.truncate(0) 
     writer.writerow(row) 
     fout.write(f.getvalue()) 

f.close() 
+0

Đối với Python 2, hãy chắc chắn sử dụng 'StringIO.StringIO()' thay vì 'io.StringIO()', nếu không bạn sẽ nhận được một lỗi mã hóa – Anconia

5

Theo docs nó có thể

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

vì vậy chúng tôi có thể sử dụng StringIO theo cách thông thường

+1

Tôi không hiểu cách sử dụng nó. Không phải là /tmp/hello.txt một tệp cục bộ, đó là những gì chúng tôi đang cố gắng tránh? – EthanP

+0

@EthanP [StringIO] (https://docs.python.org/2/library/stringio.html) - Đọc và viết chuỗi dưới dạng tệp. Sử dụng đối tượng 'StringIO' thay vì tệp –

+0

Không, theo [vé này] (https://github.com/boto/boto3/issues/256), nó không được hỗ trợ. Ý tưởng sử dụng luồng với S3 là để tránh sử dụng các tệp tĩnh khi cần để tải lên các tệp lớn có dung lượng khoảng một vài gigabyte. Tôi cũng đang cố gắng giải quyết vấn đề này - tôi cần đọc một dữ liệu lớn từ mongodb và đặt vào S3, tôi không muốn sử dụng các tệp. – baldr

Các vấn đề liên quan