4

我有以下 Werkzeug 应用程序用于将文件返回给客户端:

from werkzeug.wrappers import Request, Response

@Request.application
def application(request):    
    fileObj = file(r'C:\test.pdf','rb')
    response = Response( response=fileObj.read() )
    response.headers['content-type'] = 'application/pdf'
    return response

我要关注的部分是这个:

response = Response( response=fileObj.read() )

在这种情况下,响应大约需要 500 毫秒(C:\test.pdf是一个 4 MB 的文件。Web 服务器在我的本地计算机中)。

但是,如果我将该行重写为:

response = Response()
response.response = fileObj

现在响应大约需要 1500 毫秒。(慢 3 倍)

如果这样写:

response = Response()
response.response = fileObj.read()

现在响应大约需要 80 秒(没错,80 秒)。

为什么这3种方法有这么大的区别?
为什么第三种方法这么慢?

4

3 回答 3

8

答案很简单:

  • x.read()<- 将整个文件读入内存,效率低下
  • 设置对文件的响应:非常低效,因为该对象的协议是迭代器。因此,您将逐行发送文件。如果它是二进制的,你甚至会以随机的块大小发送它。
  • 设置response为字符串:坏主意。如前所述,它是一个迭代器,因此您现在将字符串中的每个字符作为单独的数据包发送。

正确的解决方案是将文件包装在 WSGI 服务器提供的文件包装器中:

from werkzeug.wsgi import wrap_file
return Response(wrap_file(environ, yourfile), direct_passthrough=True)

direct_passthrough标志是必需的,以便响应对象不会尝试遍历文件包装器,但不会触及 WSGI 服务器。

于 2013-05-27T23:41:53.860 回答
4

经过一些测试,我想我已经弄清楚了其中的奥秘。

@Armin 已经解释了为什么...

response = Response()
response.response = fileObj.read()

……太慢了。但这并不能解释为什么...

response = Response( response=fileObj.read() )

……好快。它们看起来是一回事,但显然它们不是。否则就不会有速度上的巨大差异。

这里的关键在于文档的这一部分:http ://werkzeug.pocoo.org/docs/wrappers/

响应可以是任何类型的可迭代或字符串。如果它是一个字符串,它被认为是一个可迭代的,其中一个项目是传递的字符串。

即当你给构造函数一个字符串时,它被转换为一个可迭代的字符串,它是唯一的元素。但是当你这样做时:response.response = fileObj.read(),字符串被视为原样。

所以要让它表现得像构造函数,你必须这样做:

response.response = [ fileObj.read() ]

现在文件被尽快发送。

于 2013-05-28T02:40:50.047 回答
1

我无法准确回答为什么会发生这种情况,但是http://werkzeug.pocoo.org/docs/wsgi/#werkzeug.wsgi.wrap_file可能有助于解决您的潜在问题。

于 2013-05-27T23:02:44.823 回答