现在的位置: 首页 > 综合 > 正文

CherryPy大文件上传

2012年07月04日 ⁄ 综合 ⁄ 共 2173字 ⁄ 字号 评论关闭
引用地址: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)

抱歉!评论已关闭.