python - 通过 Gunicorn 上传 Flask 和 NGINX 流式文件

标签 python python-3.x linux nginx flask

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

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

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

这是我启动 gunicorn 的方式:

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

这是我的 Flask 端点代码:

@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;

   }

最佳答案

谷歌搜索后我找到了large file uploads eating up memory在 werkzeug 问题中。 Ivarref 发布了 version这不会消耗大量内存。

我还没有发现文件上传处理的源代码。但是,我猜想 werkzeug(或 flask )在将文件交给用户之前将所有内容加载到内存中。通过直接处理 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 作为临时文件,这会导致将文件内容存储在内存中并占用大量内存。

关于python - 通过 Gunicorn 上传 Flask 和 NGINX 流式文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57610929/

相关文章:

python - 如何使Python/Nginx/FastCGI在更新/更改时自动重新编译代码?

c - 如何将此循环从 C 语言转换为 python 3?

python - 如何在 Django 管理中更改 "app name"?

linux - outb() 在 LDD3 示例短模块中不起作用

linux - 在插入模式下移至 VI 中当前行的行尾或行首

python - 对非数字类型的符号操作

python - 在 Python 中将 float 转换为两位数的十六进制数

javascript - 自然语言处理数据库查询

python - 如何将 DataFrame 转换为仅获取映射值的字典?

c - 如何立即调入新分配的虚拟内存