Downloading files
CherryPy allows you to serve a file from your page handler. Here is a simple recipe to handle downloads:
import glob import os.path import cherrypy from cherrypy.lib.static import serve_file class Root: def index(self, directory="."): html = """<html><body><h2>Here are the files in the selected directory:</h2> <a href="index?directory=%s">Up</a><br /> """ % os.path.dirname(os.path.abspath(directory)) for filename in glob.glob(directory + '/*'): absPath = os.path.abspath(filename) if os.path.isdir(absPath): html += '<a href="/index?directory=' + absPath + '">' + os.path.basename(filename) + "</a> <br />" else: html += '<a href="/download/?filepath=' + absPath + '">' + os.path.basename(filename) + "</a> <br />" html += """</body></html>""" return html index.exposed = True class Download: def index(self, filepath): return serve_file(filepath, "application/x-download", "attachment") index.exposed = True if __name__ == '__main__': root = Root() root.download = Download() cherrypy.quickstart(root)
Note that CherryPy is not the fastest for doing such things. If you think you'll have many and big downloads, put CP BehindApache? and let Apache serve those files.
Older versions
| replace this | with this | |
| 2.2 | cherrypy.quickstart(root) | cherrypy.root = root cherrypy.server.start() |
| from cherrypy.lib.static import serve_file | from cherrypy.lib.cptools import serveFile |
2.0
import os from cherrypy import cpg def file2Generator(f): while True: data = f.read(1024 * 8) # Read blocks of 8KB at a time if not data: break yield data class Root: def download(self, path): fStat = os.stat(path) cpg.response.headerMap["Content-Type"] = "application/x-download" cpg.response.headerMap['Content-Length'] = int(fStat.st_size) f = open(path, 'rb') return file2Generator(f) download.exposed = True cpg.root = Root() cpg.server.start()
#!/usr/bin/python # -*- coding: utf-8 -*- # Simple downloader script import sys import glob import os.path from cherrypy import cpg from cherrypy.lib.filter.basefilter import BaseOutputFilter class DownloadFilter(BaseOutputFilter): def __init__(self): self.filepath = None def beforeResponse(self): self.filepath = cpg.response.body[0] # Let's learn more about the file... fStat = os.stat(self.filepath) # CP doc states that we must call this if we write directly to the socket cpg.response.sendResponse = False # Let's define some headers. For simplicity we send only one type of content type cpg.response.headerMap["Content-Type"] = "application/x-download" # Make sure we know during the process the size of the file cpg.response.headerMap["Content-Length"] = int(fStat.st_size) # Let's tell the client we have an attachment for it cpg.response.headerMap["Content-Disposition"] = "attachment; filename=%s" % (os.path.basename(self.filepath), ) # Send now the information cpg.response.wfile.flush() def afterResponse(self): # Now send the file to the client cpg.response.wfile.writelines(file(self.filepath, 'rb').readlines()) #cp.response.flush() class Root: def index(self, directory="."): html = """<html> <head /> <body> <h2>Here are the files in the selected directory :</h2> <a href="index?directory=%s">Up</a> <br /> """ % (os.path.dirname(os.path.abspath(directory)), ) for filename in glob.glob(directory + '/*'): absPath = os.path.abspath(filename) if os.path.isdir(absPath): html += '<a href="/index?directory=' + absPath + '">' + os.path.basename(filename) + "</a> <br />" else: html += '<a href="/download/?filepath=' + absPath + '">' + os.path.basename(filename) + "</a> <br />" html += """</body></html>""" return html index.exposed = True class Download: _cpFilterList = [ DownloadFilter() ] def index(self, filepath): return filepath index.exposed = True cpg.root = Root() cpg.root.download = Download() cpg.server.start()

