使用 Flask 接收 gzip

Receiving gzip with Flask

我正在尝试从 HTTP POST 接收 gzip 压缩的 JSON 文件到 Flask (v0.10)。我觉得在尝试打开 gzip 之前可能有一些额外的数据需要删除。

这是我的代码:

from flask import Flask, jsonify, request, abort
import gzip, StringIO
app = Flask(__name__)

# Handle posted data
@app.route('/', methods = ['POST'])
def post_gzip():

    # Check for a supported media type
    if (request.headers['Content-Type'] == 'application/x-gzip'):

        file = request.data
        f = gzip.open(file, 'rb')        

        return f;

    else:
        # 415 Unsupported Media Type
        abort(415)

if __name__ == "__main__":
    app.debug = True
    app.run()

我正在使用 cURL 将压缩的 JSON 文件发布到 Flask,如下所示:

curl -X POST -d @test.json.gz http://127.0.0.1:5000/ -H "Content-Type:application/x-gzip" -H "Content-Encoding:gzip"

我收到的错误是:

UnicodeDecodeError: 'utf8' codec can't decode byte 0x8b in position 1: invalid start byte

Flask 似乎无法将接收到的数据视为 gz 文件。也许 request.data 甚至不是正确的用法。

有好心人能用这个给我指明正确的方向吗?

您导入 StringIO 但从未实际使用它并将字符串提供给需要文件名的 gzip.open。您遇到的错误是 gzip 在尝试打开文件名之前尝试将文件名解码为 Unicode。 下面利用 StringIO 制作一个可以被 gzip 使用的类文件对象:

...
fakefile = StringIO.StringIO(request.data) # fakefile is now a file-like object thta can be passed to gzip.GzipFile:
uncompressed = gzip.GzipFile(fileobj=fakefile, mode='r')
return uncompressed.read()
...

编辑: 我重构了下面的代码并添加了相关注释以便更好地理解正在发生的事情:

from flask import Flask, request
import gzip, StringIO

app = Flask(__name__)

@app.route('/', methods = ['POST'])
def my_function():

    # `request.data` is a compressed string and `gzip.GzipFile`
    # doesn't work on strings. We use StringIO to make it look
    # like a file with this:
    fakefile = StringIO.StringIO(request.data)

    # Now we can load the compressed 'file' into the 
    # `uncompressed` variable. While we're at it, we
    # tell gzip.GzipFile to use the 'rb' mode
    uncompressed = gzip.GzipFile(fileobj=fakefile, mode='rb')

    # Since StringIOs aren't real files, you don't have to 
    # close the file. This means that it's safe to return
    # its contents directly:
    return uncompressed.read()

if __name__ == "__main__":
    app.debug = True
    app.run()

已接受的答案对于 Python 2 是正确的,但以防万一您尝试使用 Python 3,您需要使用 BytesIO 而不是 StringIO:

compressed_data = io.BytesIO(request.data)
text_data = gzip.GzipFile(fileobj=compressed_data, mode='r')

对于 Python 3,我只使用 gzip.decompress(request.data) 其中 returns 一个解压缩的字符串。

这只是一个方便的 shorthand 功能,8 年前添加的:)

想看代码的可以找here.

2019 编辑:写了一个简单的flask extension你可以在你的应用程序中使用。