Flask大文件下载

3

从Flask下载文件时出现内存错误。 文件大小约为100兆字节。 我该如何修复它?

Flask下载代码

return send_from_directory(s_trash_path, s_zip_name, mimetype='zip', as_attachment=True)

错误代码

[2018-07-21 16:11:22,328] ERROR in app: Exception on /ec-fileupload/download/select [POST]
Traceback (most recent call last):
  File "/home/venv_ec_fileupload/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/venv_ec_fileupload/lib/python3.6/site-packages/flask/app.py", line 1615, in full_dispatch_request
    return self.finalize_request(rv)
  File "/home/venv_ec_fileupload/lib/python3.6/site-packages/flask/app.py", line 1632, in finalize_request
    response = self.process_response(response)
  File "/home/venv_ec_fileupload/lib/python3.6/site-packages/flask/app.py", line 1856, in process_response
    response = handler(response)
  File "./app/__init__.py", line 170, in after_request
    s_data = resp.get_data()
  File "/home/venv_ec_fileupload/lib/python3.6/site-packages/werkzeug/wrappers.py", line 987, in get_data
    rv = b''.join(self.iter_encoded())
MemoryError
2个回答

15

如果您提供二进制文件,那么您不应该通过迭代行来读取文件,因为它实际上只包含一条“行”,这意味着您仍然会一次性将整个文件加载到RAM中。

读取大型文件的唯一正确方法是使用块:

CHUNK_SIZE = 8192
def read_file_chunks(path):
    with open(path, 'rb') as fd:
        while 1:
            buf = fd.read(CHUNK_SIZE)
            if buf:
                yield buf
            else:
                break

那么可以安全地在这个块读取器上调用 stream_with_context,例如,如果您提供视频文件:

@app.route('/videos/<name>')
def serve_video(name):
    fp = resource_path_for(name)
    if fp.exists():
        return Response(
            stream_with_context(read_file_chunks(fp)),
            headers={
                'Content-Disposition': f'attachment; filename={name}'
            }
        )
    else:
        raise exc.NotFound()

在 Flask 响应过程的底层,它会将每个块(从生成器 read_file_chunks(fp) 中获取)刷新到连接中,然后再加载下一个块。刷新后,这个块的数据就不再被引用,并且由垃圾收集器清理掉,因此在同一时间内不会有太多的块停留在 RAM 中。


4

由于您的文件很大且是动态生成的,建议您不要使用send_from_directory()来发送文件。

请查看Flask流媒体文档,了解如何流式传输文件(发送小块数据而不是整个文件):http://flask.pocoo.org/docs/1.0/patterns/streaming/

from flask import Response

@app.route('/large.csv')
def generate_large_csv():
    def generate():
        for row in iter_all_rows():
            yield ','.join(row) + '\n'
    return Response(generate(), mimetype='text/csv')

上面的代码片段展示了如何使用Flask流式传输csv文件。但是,如果您的文件是静态的,那么Flask建议使用nginx进行部署。

谢谢你的回答。 我还是遇到了内存错误。 这是发生了。 我的代码有问题吗?\n def generate(): \n with open(s_path, 'rb') as r: for line in r: yield str(line) response = Response(generate(),mimetype='application/zip') response.headers['Content-Type'] = "application/octet-stream" response.headers['Content-Disposition'] = "inline; filename=" + os.path.basename(s_path) return response - undefined
这样的话,浏览器将在所有迭代完成之前不会做出响应,而且浏览器需要很长很长很长...时间才能做出响应。而且网关很可能会超时。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接