在cherrypy中下载大文件

oll*_*ien 2 python nginx cherrypy download uwsgi

我正在使用Cherrypy托管文件访问类型网站,通过upsgi和nginx在Raspberry Pi上托管.我注意到的一件事是,如果文件相当大(比方说,大约一千兆字节),uwsgi说它被信号9杀死了.这可以通过放一个来cherrypy.config.update({'tools.sessions.timeout': 1000000})解决,但这并没有真正解决问题,因为它是一个糟糕的hacky解决方法,并没有真正起作用.它主要是通过使超时非常大来引起另一个问题.此外,浏览器无法准确估计需要多长时间,并且最终会挂起一段时间(阅读:硬连线连接上大约5分钟),然后快速开始下载.

它始于

文件下载

然后去

继续文件下载

我的下载代码非常简单,只包含这一行.

return cherrypy.lib.static.serve_file(path,"application/x-download",os.path.basename(path))

我之前的下载代码并不是很好用.

f = file(path) cherrypy.response.headers['Content-Type'] = getType(path)[0] return f 有办法解决这个问题吗?

saa*_*aaj 5

一般考虑

首先,我要说的是这样一个堆积的配置,CherryPy - > uWSGI - > Nginx,对于这样一个受限制的环境.根据作者的说法,当没有特殊要求时,可以安全地将CherryPy用于小型应用程序.在前面添加Nginx会增加很多灵活性,所以它通常是有益的,但只要CherryPy的默认部署是标准HTTP,我强烈建议保持两者(并完全忘记WSGI).

其次,您可能已经知道您的问题可能与会话相关,考虑到您尝试过的解决方法.以下是有关文件下载的流媒体响应正文的文档引用.

通常,不流输出更安全,更容易.因此,默认情况下,流输出是关闭的.流输出和使用会话需要很好地理解会话锁的工作方式.

它建议的是手动会话锁管理.了解您的应用程序如何工作应该引导您进行适当的锁定设计.

第三.通常有一种方法可以将处理文件下载的任务转移到Web服务器,基本上是通过从代理应用程序发送带有文件名的适当头文件.在nginx的情况下,它被调用X-accel.因此,您可以避免锁定管理的麻烦,仍然有会话限制下载.

实验

我已经制作了一个简单的CherrPy应用程序,有两个下载选项,并将它推迟Nginx之后.我在FirefoxChromium的本地Linux机器上玩了1.3GiB视频文件.有三种方式:

  1. 从CherryPy(http://127.0.0.1:8080/native/video.mp4)下载非代理,
  2. 通过Nginx(http://test/native/video.mp4)从CherryPy下载代码,
  3. 通过Nginx(http://test/nginx/video.mp4)从CherryPy下载X-accel .

使用(1)和(2)我在Firefox和Chromium中都有一些奇怪的行为.(1)在Firefox上有几天的正常运行时间我总是有〜5MiB/s的下载速度和一个满载的CPU核心.在新的Firefox上没有这样的行为.(2)在Chromium上导致了几个未完成的中断下载(所有时间都在1GiB左右).但总的来说,两款浏览器都显示出硬盘的物理性能为50-70MiB/s.

随着(3)我在两者中没有问题,相同的50-70MiB/s吞吐量,所以在我的小实验中,它最终成为最稳定的方式.

建立

app.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os

import cherrypy


DownloadPath = '/home/user/Videos'

config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8
  }
}


class App:

  @cherrypy.expose
  def index(self):
    return 'Download test'

  @cherrypy.expose
  def native(self, name):
    basename = os.path.basename(name)
    filename = os.path.join(DownloadPath, basename)
    mime     = 'application/octet-stream'
    return cherrypy.lib.static.serve_file(filename, mime, basename)

  @cherrypy.expose
  def nginx(self, name):
    basename = os.path.basename(name)
    cherrypy.response.headers.update({
      'X-Accel-Redirect'    : '/download/{0}'.format(basename),
      'Content-Disposition' : 'attachment; filename={0}'.format(basename),
      'Content-Type'        : 'application/octet-stream'
    })


if __name__ == '__main__':
  cherrypy.quickstart(App(), '/', config)
Run Code Online (Sandbox Code Playgroud)

app.conf

server {
  listen  80;

  server_name test;

  root /var/www/test/public;

  location /resource {
    # static files like images, css, js, etc.
    access_log off;
  }

  location / {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  }

  location /download {
    internal;
    alias /home/user/Videos;
  }

}
Run Code Online (Sandbox Code Playgroud)