引用地址:
http://tools.cherrypy.org/wiki/DirectToDiskFileUpload 和上篇文件中介绍的CherryPy文件上传的主要区别在于:
- 过滤器由一个工具取代,禁用CherryPy的请求体处理
- 更改默认超时
- 改变默认请求body大小的限制
- 临时文件由cgi.FieldStorage改为tempfile.NamedTemporaryFile,以避免HTTP上传后的文件的拷贝,这在考虑处理大文件的速度和空间效率时,是非常重要的。
示例代码:
#!/usr/bin/python2.6
import cherrypy
import cgi
import tempfile
import os
class myFieldStorage(cgi.FieldStorage):
"""我们的版本使用一个命名的临时文件,而不是默认
非命名文件;保持可可见,使我们能够上传完毕后进一步处理"""
def make_file(self, binary=None):
return tempfile.NamedTemporaryFile(delete=False)
def noBodyProcess():
"""通过设置cherrypy.request.process_request_body = False, 可以让我们直接控制文件的输出
默认是加载到内存,我们直接输出到磁盘"""
cherrypy.request.process_request_body = False
cherrypy.tools.noBodyProcess = cherrypy.Tool('before_request_body', noBodyProcess)
class fileUpload:
"""fileUpload cherrypy application"""
@cherrypy.expose
def index(self):
"""简单的HTML上传Form"""
return ""
<form action="upload" method="post" enctype="multipart/form-data">
File: <input type="file" name="theFile"/> <br/>
<input type="submit"/>
</form>
"""
@cherrypy.expose
@cherrypy.tools.noBodyProcess()
def upload(self, theFile=None):
"""上传Action"""
# 大文件传输可能需要很长的时间,cherrypy默认的响应时间是300s,在这里我们设置为1小时。
cherrypy.response.timeout = 3600
# 将请求头的Key转换为小写
lcHDRS = {}
for key, val in cherrypy.request.headers.iteritems():
lcHDRS[key.lower()] = val
# 可以在这里限制上传内容的长度...
# incomingBytes = int(lcHDRS['content-length'])
# 使用自定义的FiledStorage分析编码后的Form数据,包括上传的文件
formFields = myFieldStorage(fp=cherrypy.request.rfile,
headers=lcHDRS,
environ={'REQUEST_METHOD':'POST'},
keep_blank_values=True
theFile = formFields['theFile']
os.link(theFile.file.name, '/tmp/'+theFile.filename)
return "ok, got it filename='%s'" % theFile.filename
# 去除请求body大小限制,cherrypy默认大小为100MB
cherrypy.server.max_request_body_size = 0
# 设置Socket连接超时时间,cherrypy默认为10s
cherrypy.server.socket_timeout = 60
cherrypy.quickstart(fileUpload())
注意: 使用FieldStorage,当文件大小小于1000字节时,其不是使用FieldStorage的make_file存储成文件,而是使用内存文件对象(CStringIO/StringIO)存储。 核心改进代码如下:
if formFields.has_key('theFile'):
theFile = formFields['theFile']
else:
print 'No theFile Field'
return 'error'
if not theFile.file:
print "Error: not a file upload."
return 'error'
elif not hasattr(theFile.file, 'name'): #手动处理小文件
name = tempfile.mktemp()
with open(name, 'wb') as f:
f.write(theFile.file.read())
else:
name = theFile.file.name
os.link(name, '/tmp/'+theFile.filename)