Search code examples
pythoncherrypy

cherry py auto download file


I'm currently building cherry py app for my projects and at certain function I need auto starting download a file.

After zip file finish generating, I want to start downloading to client So after images are created, they are zipped and sent to client

class Process(object):
    exposed = True

    def GET(self, id, norm_all=True, format_ramp=None):
        ...
        def content(): #generating images
            ...

            def zipdir(basedir, archivename):
                assert os.path.isdir(basedir)
                with closing(ZipFile(archivename, "w", ZIP_DEFLATED)) as z:
                    for root, dirs, files in os.walk(basedir):
                        #NOTE: ignore empty directories
                        for fn in files:
                            absfn = os.path.join(root, fn)
                            zfn = absfn[len(basedir)+len(os.sep):] #XXX: relative path
                            z.write(absfn, zfn)

            zipdir("/data/images/8","8.zip")

            #after zip file finish generating, I want to start downloading to client
            #so after images are created, they are zipped and sent to client
            #and I'm thinking do it here, but don't know how

        return content()

    GET._cp_config = {'response.stream': True}


    def POST(self):
        global proc
        global processing
        proc.kill()
        processing = False

Solution

  • Just create a zip archive in memory and then return it using file_generator() helper function from cherrypy.lib. You may as well yield HTTP response to enable streaming capabilities (keep in mind to set HTTP headers prior to doing that). I wrote a simple example (based on your snippet) for you just returning a whole buffered zip archive.

    from io import BytesIO
    
    import cherrypy
    from cherrypy.lib import file_generator
    
    
    class GenerateZip:
        @cherrypy.expose
        def archive(self, filename):
            zip_archive = BytesIO()
            with closed(ZipFile(zip_archive, "w", ZIP_DEFLATED)) as z:
                for root, dirs, files in os.walk(basedir):
                    #NOTE: ignore empty directories
                    for fn in files:
                        absfn = os.path.join(root, fn)
                        zfn = absfn[len(basedir)+len(os.sep):] #XXX: relative path
                        z.write(absfn, zfn)
    
    
            cherrypy.response.headers['Content-Type'] = (
                'application/zip'
            )
            cherrypy.response.headers['Content-Disposition'] = (
                'attachment; filename={fname}.zip'.format(
                    fname=filename
                )
            )
    
            return file_generator(zip_archive)
    

    N.B. I didn't test this specific piece of code, but the general idea is correct.