CherryPy 2.1 or better
The recipes on this page apply to CP 2.0. If you want to do file uploads in CP 2.1 or better, see cherrypy/tutorial/tut09_files.py.
Simple case
Here is a simple example that shows how file uploads are handled by CherryPy
from cherrypy import cpg class Root: def index(self): return """ <html><body> <form action="upload" method="post" enctype="multipart/form-data"> filename: <input type="file" name="myFile"/><br/> <input type="submit"/> </form> </body></html> """ index.exposed = True def upload(self, myFile): return """ <html><body> myFile length: %s<br/> myFile filename: %s<br/> myFile mime-type: %s </body></html> """ % ( len(myFile), cpg.request.filenameMap['myFile'], cpg.request.fileTypeMap['myFile'] ) upload.exposed = True cpg.root = Root() cpg.server.start()
As you can see, the file is being passed to the method as a regular string, and you have access to the filename and mime-type as well.
Streaming the uploaded file to disk (Updated)
If you are planning to deal with large files, then it might not be a good idea to store the file in a python string. In that case, you can write some code to stream this file directly to disk instead.
The StreamFilter? was updated to prevent server lock-ups in the case of get requests. This would happen if a user types in "http://localhost/postFile".
Here is some code that shows how to do it:
from cherrypy import cpg from cherrypy.lib.filter import basefilter import cgi class StreamFilter(basefilter.BaseInputFilter): def afterRequestHeader(self): if cpg.request.path == '/postFile': # if you don't check that it is a post method the server might lock up # we also check to make shour something was submitted if not 'Content-Length' in cpg.request.headerMap or cpg.request.method != 'POST': """ the file is empty you might want to redirect""" else: # Tell CherryPy not to parse the POST data itself for this URL cpg.request.parsePostData = False class Root: _cpFilterList = [StreamFilter()] def index(self): return """ <html><body> <form method=post action=postFile enctype="multipart/form-data"> Upload a file: <input type=file name=myFile><br> <input type=submit> </form> </body></html> """ index.exposed = True def postFile(self): # Note: we could check Content-Length here and bail out if it's too big # Create a copy of cpg.request.headerMap with lower keys lowerHeaderMap = {} for key, value in cpg.request.headerMap.items(): lowerHeaderMap[key.lower()] = value # Use cgi.FieldStorage to parse the POST data. # This will store the uploaded file in a tempfile dataDict = cgi.FieldStorage(fp=cpg.request.rfile, headers=lowerHeaderMap, environ={'REQUEST_METHOD':'POST'}, keep_blank_values=1) value = dataDict['myFile'] # Value has 2 attributes: # - filename contains the name of the uploaded file # - file is an input stream opened for reading # Read the tempfile and store it in the final file # Note that you'd have to carefully choose the filename if you want to # be thread-safe f = open('/tmp/myFile', 'wb') while 1: data = value.file.read(1024 * 8) # Read blocks of 8KB at a time if not data: break f.write(data) f.close() return "<html><body>The file has been saved in /tmp/myFile</body></html>" postFile.exposed = True cpg.root = Root() cpg.server.start()
You can try to run that code, upload a huge file and watch the RAM of your process not moving :-)
As an alternative to the while loop one could use shutil.copyfileobj, which is same code but you would gain any optimization work on shutil.
from shutil import copyfileobj ... data.seek(0) # just to ensure we're at the beginning copyfileobj(fsrc=data, fdst=f, length=1024 * 8) f.close()
edited for clarity for noobs like me
from shutil import copyfileobj ... f = open('/tmp/myFile', 'wb') data = value.file data.seek(0) # just to ensure we're at the beginning copyfileobj(fsrc=data, fdst=f, length=1024 * 8) f.close()
File Upload Filter
[This filter is supposed to work in CP 2.0 only, 2.1 is going to have such functionality on board]
This filter automatically stores uploaded file under a temporary name in a given directory. Requires also guid.py module by C. Albrecht (included). In the following example simple form is used to store file on the server and display its real and temporary name as well as file type recognized by server:
from cherrypy import cpg
from fileuploadfilter import FileUploadFilter
class Test:
TempDir = 'c:\\temp\\' #directory to store uploaded files
#UploadContext is the list of following tuples:
#(exposed method name, parameter used to transfer file, temporary directory name)
UploadContext = [('processUpload', 'fileob', TempDir)]
_cpFilterList = [FileUploadFilter(UploadContext)]
@cpg.expose
def index(self):
return """<HTML><BODY>
<FORM ACTION="processUpload" METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="file" name="fileob">
<INPUT TYPE="submit">
</FORM>
</BODY></HTML>
"""
@cpg.expose
def processUpload(self, fileob):
yield 'File Name: %s<br>' % cpg.request.filenameMap['fileob']
yield 'File Type: %s<br>' % cpg.request.fileTypeMap['fileob']
yield 'Stored as: %s' % fileob
Attachments
- streamfilter.py (2.5 kB) -
StreamFilter? for CP 2.1
, added by fumanchu on 08/29/05 15:42:51. - fileuploadfilter.py (3.3 kB) - added by fumanchu on 09/04/06 00:03:58.
- guid.py (6.1 kB) - added by fumanchu on 09/04/06 00:04:08.

