【问题标题】:Flask and NGINX Streaming File Upload through Gunicorn通过 Gunicorn 上传 Flask 和 NGINX 流文件
【发布时间】:2019-12-27 20:55:46
【问题描述】:

我在 Linux ( 4.9.13 ) 上使用 Flask ( 1.0.2 ) 和 Python 3.7 以及由 NGINX ( 1.15.7 ) 代理的 Gunicorn ( 19.9.0 )。

我可以使用以下代码成功地将一个大 (1.2GB) 文件上传到我的 Flask 服务器。但是,整个文件在写入磁盘之前会在 RAM 中缓冲,然后使用如下所示的 file.save() 函数。我试过谷歌搜索,发现各种帖子据称将文件流式传输到磁盘而不是在 RAM 中缓冲,但我无法让他们的方法起作用。

如何让文件直接流式传输到磁盘,而不是先在 RAM 中缓冲,然后再到磁盘?谢谢。

这是我启动 gunicorn 的方式:

gunicorn --workers=4 --threads=8 --bind localhost:8000 StartFlaskServer:app

这是我的 Flask Endpoint 代码:

@app.route("/firmware_update", methods=["GET", "POST"])
def upload_video():

    if request.method == "POST":

        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')

            return make_response(jsonify({"message": "No File Part Specified!"}), 500)

        file = request.files['file']

        # if user does not select file, browser also
        # submit an empty part without filename
        if file.filename == '':
            flash('No selected file')

            return make_response(jsonify({"message": "No Selected File!"}), 500)

        if not allowed_file(file.filename):

            suffix = file.filename.rsplit('.', 1)[1].lower()
            return make_response(jsonify({"message": "Filetypes of %s not accepted ( Must be of type: %s )!"%(suffix,ALLOWED_EXTENSIONS)}), 500)

        fileFullPath = os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(request.files['file'].filename))

        # Why does this not work? Cannot convert file to stream type with no buffering in NGINX?
        # with open(fileFullPath, "wb") as f:
        #    chunk_size = 4096
        #    while True:
        #        chunk = request.stream.read(chunk_size)
        #        print("Flask Writing Chunk: %s"%(len(chunk)))
        #        if len(chunk) == 0:
        #            break
        #        print("Wrote this much: %s"%(f.write(chunk)))

        file.save(fileFullPath)

        return make_response(jsonify({"message": "File uploaded"}), 200)

    return render_template("upload_firmware_bundle.html")

这是我的 NGINX 端点配置:

   # Proxy upload
   location /firmware_update {

       # Proxy config
       proxy_pass http://localhost:8000;
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

       # Do not buffer body
       client_max_body_size 0;
       proxy_http_version 1.1;
       proxy_buffering off;
       proxy_request_buffering off;

   }

【问题讨论】:

标签: python python-3.x linux nginx flask


【解决方案1】:

经过一番谷歌搜索后,我在 werkzeug 问题中找到了 large file uploads eating up memory。 Ivarref 发了一个version,不会消耗很多内存。

我还没有发现文件上传处理的源代码。但是,我猜 werkzeug(或烧瓶)在将文件交给用户之前会将所有内容加载到内存中。通过直接处理 request.environ 我们绕过了该逻辑并避免消耗过多的内存。

编辑:查看了werkzeug的源码,发现如果访问Request.files,会调用Request._load_form_data,然后会创建Request.form_data_parser_class的实例。 Request.form_data_parser_class 的默认值为werkzeug.FormParser,它使用default_stream_factory 作为流工厂。似乎在某些情况下,default_stream_factory 会作为临时文件回退到BytesIO,这会导致将文件内容存储在内存中并占用大量内存。

【讨论】:

    猜你喜欢
    • 2015-05-24
    • 1970-01-01
    • 2021-05-12
    • 2016-08-30
    • 2016-03-02
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    • 1970-01-01
    相关资源
    最近更新 更多