【问题标题】:Flask large file downloadFlask 大文件下载
【发布时间】:2018-07-21 07:17:02
【问题描述】:

从 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

【问题讨论】:

    标签: flask download


    【解决方案1】:

    如果您提供二进制文件,则不应遍历行,因为它基本上只包含一个“行”,这意味着您仍然可以一次将整个文件全部加载到 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))并在加载下一个块之前将其刷新到连接。刷新后,chunk数据不再被引用并被垃圾收集器清理,因此不会有多少chunk同时停留在RAM中。

    【讨论】:

      【解决方案2】:

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

      查看有关如何流式传输文件(发送小块数据而不是完整文件)的烧瓶流式传输文档: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文件的sn-p。

      但是,如果您的文件是静态的,那么 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) 返回响应
      猜你喜欢
      • 1970-01-01
      • 2023-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-14
      相关资源
      最近更新 更多