李迎辉 2009年10月17日 星期六 11:11 | 1837次浏览 | 0条评论
根据邮件列表中的回复加工
1. 提出问题:
使用webob在处理通过fancyupload上传的文件,发现会系统挂起,但只要先把request.body读出来就没有问题。因此在uliweb中,将原来使用的webob去除,改成了werkzeug来处理了。
2. qiangninghong的试验:
因为limodou反应的这个问题认为是cgi模块有bug导致的,所以很重要。今天晚上稍稍有点时间,写了一个测试程序来验证一下,但是发现似乎没有这样的情况?
测试代码在这里 http://code.google.com/p/hongqnlib/source/browse/
就是照着 FancyUpload 的 Attach File demo 页面 ( http://digitarald.de/project/fancyupload/3-0/showcase/attach-a-file/ ) 翻译了一下。运行 server.py 后,访问 http://localhost:5002/ ,上传了几个文件,都没有出现挂住的情况啊?
难道是我的这个测试有什么问题?请 limodou 帮忙看看。
3. 我的试验:
我把你的程序改了一下:
import os
import mimetypes
import time
import socket
from hashlib import md5
import simplejson as json
import webob, webob.exc
from paste.urlparser import StaticURLParser
from paste.cascade import Cascade
import logging
from paste import fileapp
def app(environ, start_response):
request = webob.Request(environ)
if request.method == 'GET':
filename = request.path_info.lstrip('/')
response = fileapp.FileApp(filename)
else:
# logging.error( repr(request.body))
filedata = request.POST['Filedata']
r = dict(status='1', name=filedata.filename)
text = filedata.file.read()
r['hash'] = md5(text).hexdigest()
if request.params.get('response') == 'xml':
response = webob.Response(content_type='text/xml')
print >>response.body_file, '<response>'
for k, v in r.items():
print >>response.body_file, \
"<%(k)s><![CDATA[%(v)s]]></%(k)s>" % locals()
print >>response.body_file, '</response>'
else:
response = webob.Response(content_type='application/json',
body=json.dumps(r))
return response(environ, start_response)
#static_app = StaticURLParser('/static')
#app = Cascade([static_app, app], (404, 405))
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('localhost', 5002, app)
server.serve_forever()
分析如下:
1. 使用你的程序的确没有问题,使用我改的程序就有问题
2. 两个差别在于你在调用处理前使用了一个StaticURLParser,而我改后的程序是不使用它,在app中自已处理。这样的目的并不是使用StaticURLParser不行,而是这样的做法其实是在app处理前多了一层处理,那么这层处理会影响后面的处理,掩盖问题的发生。
3. 在我的例子中,只要把# logging.error( repr(request.body))这行的注释去了,程序就正常。
4. 原因就是,只要你想办法在执行request.POST['Filedata']之前执行了象request.body之类的代码,这样会引起对body的整个读取,这时读取是按Content-Length来处理的,并且会放在缓冲区中,因此后续的处理就是从这个缓冲区来的了,因此缓冲区最后有没有回车换行是没有关系的。而使用flash控件上传文件,文件体示例如下:
ERROR:root:'------------ei4GI3ei4Ef1Ef1GI3ei4Ij5GI3GI3\r\nContent-Disposition:
form-data; name="Filename"\r\n\r\na.c\r\n------------ei4GI3ei4Ef1Ef1GI3ei4Ij5GI3G
I3\r\nContent-Disposition: form-data; name="Filedata";
filename="a.c"\r\nContent-Type:
application/octet-stream\r\n\r\n#include
<stdio.h>\r\n\r\n------------ei4
GI3ei4Ef1Ef1GI3ei4Ij5GI3GI3\r\nContent-Disposition: form-data;
name="Upload"\r\n\r\nSubmit
Query\r\n------------ei4GI3ei4Ef1Ef1GI3ei4Ij5GI3GI3--'
可以看到,最后是没有\r\n的。这不是浏览器的问题,是flash组件本身的问题,相关的flash的类本身的问题。但其实也不算一个问题,因为cgi的处理是基于multipart的boundary需要以\r\n结束这个假设,但这种假设在某些情况下有问题。相应的cgi的代码如下(从cgi.py中找到的703行):
def read_lines_to_eof(self):
"""Internal: read lines until EOF."""
while 1:
line = self.fp.readline(1<<16)
if not line:
self.done = -1
break
self.__write(line)
就是这个readline造成的,如果没有\r\n它根本不会返回。
因此前面的问题就是,如果不先执行request.body,则后面的request.POST['Filedata']会从流中进行读取,而不是缓冲区中,但flash正好又不返回\r\n,因此server在使用readline就停处了,造成挂起,进而引发浏览器的flash控件的超时。
而你的例子使用了StaticURLParser,可能会进行request.body的读取,所以掩盖了这个问题。当然它到底怎么做的,我没有继续深究。因此我改了你的程序,就是为了不让其它的因素干扰,问题就暴露出来了。
Zeuux © 2024
京ICP备05028076号
暂时没有评论