2017-05-25 13 views
7

Tôi có thể tạo và phát trực tiếp văn bản, nhưng không thể tạo và phát trực tuyến tệp nén khi đang di chuyển.Tạo và phát tệp nén bằng Flask

from flask import Flask, request, Response,stream_with_context 
import zlib 
import gzip 

app = Flask(__name__) 

def generate_text(): 
    for x in xrange(10000): 
     yield "this is my line: {}\n".format(x) 

@app.route('/stream_text') 
def stream_text(): 
    response = Response(stream_with_context(generate_text())) 
    return response 

def generate_zip(): 
    for x in xrange(10000): 
     yield zlib.compress("this is my line: {}\n".format(x)) 

@app.route('/stream_zip') 
def stream_zip(): 
    response = Response(stream_with_context(generate_zip()), mimetype='application/zip') 
    response.headers['Content-Disposition'] = 'attachment; filename=data.gz' 
    return response 

if __name__ == '__main__': 
    app.run(host='0.0.0.0', port=8000, debug=True) 

Thần sử dụng curl và gunzip:

curl http://127.0.0.1:8000/stream_zip > data.gz 

gunzip data.gz 
gunzip: data.gz: not in gzip format 

Tôi không quan tâm nếu nó là zip, gzip, hoặc bất kỳ loại khác của nén.

generate_text trong mã thực của tôi tạo ra hơn 4 GB dữ liệu vì vậy tôi muốn nén khi đang di chuyển.

Lưu văn bản thành tệp, nén, trả về tệp zip và xóa hơn không phải là giải pháp mà tôi theo sau.

Tôi cần tạo vòng lặp tạo một số văn bản -> nén văn bản đó -> dữ liệu đã nén trực tuyến cho đến khi tôi hoàn tất.

zip/gzip ... mọi thứ đều ổn miễn là nó hoạt động.

+0

Tôi đoán rằng vì zlib.compress trả về một chuỗi, nên bạn không gửi mã byte gzip thực. –

+0

@DrorHilman: đây là Python 2, vì vậy cả dữ liệu gzip và dữ liệu nén zlib thô đều là chuỗi. Nhưng có, bạn không thể sử dụng gzip để giải nén dữ liệu zlib thô trực tiếp, ít nhất là không có hackery (xem [Cách giải nén dữ liệu zlib trong UNIX?] (// unix.stackexchange.com/q/22834)). –

Trả lời

-1

Tôi nghĩ rằng hiện tại bạn chỉ cần gửi máy phát điện thay vì dữ liệu! Bạn có thể muốn làm điều gì đó như thế này (tôi đã không kiểm tra nó, vì vậy có thể cần một số thay đổi):

def generate_zip(): 
    import io 
    with gzip.GzipFile(fileobj=io.BytesIO(), mode='w') as gfile: 
     for x in xrange(10000): 
      gfile.write("this is my line: {}\n".format(x)) 
    return gfile.read() 
+0

Đó là * toàn bộ điểm * của bộ tạo; nó được lặp lại ở nơi khác. Vấn đề là sử dụng 'zlib.compress()'. Giải pháp của bạn sẽ loại bỏ khả năng phát trực tuyến hoàn toàn. –

-1

Làm việc generate_zip() với mức tiêu thụ bộ nhớ thấp :):

def generate_zip(): 
    buff = io.BytesIO() 
    gz = gzip.GzipFile(mode='w', fileobj=buff) 
    for x in xrange(10000): 
     gz.write("this is my line: {}\n".format(x)) 
     yield buff.read() 
     buff.truncate() 
    gz.close() 
    yield buff.getvalue() 
+0

Điều này không thực sự trả về bất kỳ dữ liệu nào cho đến khi sản lượng cuối cùng. – mattjvincent

+0

Điều này mang lại 10k chuỗi rỗng, sau đó là toàn bộ tài liệu. Đây cũng là quá mức cần thiết. Không cần phải giữ các đối tượng tệp riêng biệt trong bộ nhớ, chỉ cần truyền trực tiếp nén trực tiếp. Nếu bạn muốn sử dụng một đối tượng 'GzipFile()', tại sao không chỉ nắm bắt các cuộc gọi 'write()' trực tiếp thay vì sử dụng một đối tượng 'BytesIO()'? –

+0

Cách tiếp cận của bạn chỉ có thể được sửa nếu bạn sử dụng 'buff.getvalue()' trong vòng lặp (không phải 'buff.read()'), được sử dụng 'buff.truncate (0)' (bạn đang cắt ngắn đến vị trí hiện tại, không để bắt đầu). Để tránh tạo ra các khối rỗng, bạn có thể kiểm tra nếu 'buff.getvalue()' thực sự tạo ra bất cứ thứ gì, và chỉ có năng suất và cắt bớt nếu nó đã làm. –

9

Bạn đang có lãi suất một loạt tài liệu nén , không phải là một luồng nén duy nhất. Không sử dụng zlib.compress(), nó bao gồm tiêu đề và tạo thành một tài liệu.

Bạn cần phải tạo một zlib.compressobj() object thay vào đó, và sử dụng Compress.compress() method trên đối tượng đó để tạo ra một dòng dữ liệu (tiếp theo là một cuộc gọi cuối cùng để Compress.flush()):

def generate_zip(): 
    compressor = zlib.compressobj() 
    for x in xrange(10000): 
     chunk = compressor.compress("this is my line: {}\n".format(x)) 
     if chunk: 
      yield chunk 
    yield compressor.flush() 

Máy nén có thể sản xuất khối rỗng khi có không đủ dữ liệu để tạo ra một đoạn dữ liệu nén đầy đủ, sản lượng trên chỉ có hiệu suất nếu thực sự có bất cứ điều gì để gửi. Bởi vì dữ liệu đầu vào của bạn rất lặp đi lặp lại và do đó dữ liệu có thể được nén hiệu quả, điều này chỉ mang lại 3 lần (một lần với tiêu đề 2 byte, một lần với khoảng 21kb dữ liệu nén bao gồm các lần lặp 8288 đầu tiên trên xrange() và cuối cùng là 4kb cho phần còn lại của vòng lặp).

Tổng hợp, điều này tạo ra cùng một dữ liệu như một cuộc gọi zlib.compress() duy nhất với tất cả các đầu vào được ghép nối. Loại mime chính xác cho định dạng dữ liệu này là application/zlib, không phảiapplication/zip.

Định dạng này không dễ dàng giải nén được với gzip tuy nhiên, không phải without some trickery. Đó là vì ở trên chưa tạo ra tệp GZIP, nó chỉ tạo luồng nén zlib thô.Để làm cho nó GZIP tương thích, bạn cần phải configure the compression correctly, gửi một tiêu đề đầu tiên và thêm một giá trị chiều dài CRC checksum và dữ liệu ở cuối:

import zlib 
import struct 
import time 

def generate_gzip(): 
    # Yield a gzip file header first. 
    yield (
     '\037\213\010\000' + # Gzip file, deflate, no filename 
     struct.pack('<L', long(time.time())) + # compression start time 
     '\002\377' # maximum compression, no OS specified 
    ) 

    # bookkeeping: the compression state, running CRC and total length 
    compressor = zlib.compressobj(
     9, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) 
    crc = zlib.crc32("") 
    length = 0 

    for x in xrange(10000): 
     data = "this is my line: {}\n".format(x) 
     chunk = compressor.compress(data) 
     if chunk: 
      yield chunk 
     crc = zlib.crc32(data, crc) & 0xffffffffL 
     length += len(data) 

    # Finishing off, send remainder of the compressed data, and CRC and length 
    yield compressor.flush() 
    yield struct.pack("<2L", crc, length & 0xffffffffL) 

Phục vụ này như application/gzip:

@app.route('/stream_gzip') 
def stream_gzip(): 
    response = Response(stream_with_context(generate_gzip()), mimetype='application/gzip') 
    response.headers['Content-Disposition'] = 'attachment; filename=data.gz' 
    return response 

và kết quả có thể được giải nén khi đang bay:

curl http://127.0.0.1:8000/stream_gzip | gunzip -c | less 
+0

Martin - relooking lúc này ... sẽ một nén khác với zlib được dễ dàng hơn? Tôi vừa chuyển đổi sang Python3. Cảm ơn! – mattjvincent

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