20

I have a small project that would be perfect for Google App Engine. Implementing it hinges on the ability to generate a ZIP file and return it.

Due to the distributed nature of App Engine, from what I can tell, the ZIP file couldn't be created "in-memory" in the traditional sense. It would basically have to be generated and and sent in a single request/response cycle.

Does the Python zip module even exist in the App Engine environment?

Soviut
  • 88,194
  • 49
  • 192
  • 260

3 Answers3

33

zipfile is available at appengine and reworked example follows:

from contextlib import closing
from zipfile import ZipFile, ZIP_DEFLATED

from google.appengine.ext import webapp
from google.appengine.api import urlfetch

def addResource(zfile, url, fname):
    # get the contents      
    contents = urlfetch.fetch(url).content
    # write the contents to the zip file
    zfile.writestr(fname, contents)

class OutZipfile(webapp.RequestHandler):
    def get(self):
        # Set up headers for browser to correctly recognize ZIP file
        self.response.headers['Content-Type'] ='application/zip'
        self.response.headers['Content-Disposition'] = \
            'attachment; filename="outfile.zip"'    

        # compress files and emit them directly to HTTP response stream
        with closing(ZipFile(self.response.out, "w", ZIP_DEFLATED)) as outfile:
            # repeat this for every URL that should be added to the zipfile
            addResource(outfile, 
                'https://www.google.com/intl/en/policies/privacy/', 
                'privacy.html')
            addResource(outfile, 
                'https://www.google.com/intl/en/policies/terms/', 
                'terms.html')
myroslav
  • 3,703
  • 23
  • 29
  • 1
    Keep in mind the response size limit on App Engine is 10MB so you cannot return a zip file larger than that. Perhaps using the new Files API (SDK 1.4.3) you could create the zip file, store it in the blobstore, then return the blob. – Bryce Cutt Apr 10 '11 at 10:29
  • This answer fails at 'buf=zipf.read(2048)'; there's no earlier reference to 'zipf'; use the answer below – Justin Apr 24 '12 at 10:19
  • @BryceCutt does Google Cloud storage change the picture at all? – mvanveen Apr 25 '12 at 05:38
  • @mvanveen you can write a zip file to Google Cloud Storage much like the blobstore using the Files API (https://developers.google.com/appengine/docs/python/googlestorage/) however unless you need some other feature of Cloud Storage (like sharing between apps) I don't see the benefit of it over the blobstore. It's nice to have options though. – Bryce Cutt Apr 26 '12 at 03:07
  • 1
    If you are copy-pasting this and using other data (especially data direct from the datastore), make sure to encode the filename and the contents, for example `contents.encode('utf-8')` as there is no standard and it may lead to errors – Morris Fauntleroy Dec 29 '13 at 21:32
  • 1
    I keep getting: AttributeError: 'Response' object has no attribute 'tell' - Any ideas? – Chris Dec 21 '16 at 10:52
  • 1
    Webapp is not available in GAE anymore. According to the webapp2 official docs: "The response buffers all output in memory, then sends the final output when the handler exits. webapp2 does not support streaming data to the client." So, it seems that this code is not actually streaming the response as the answer suggests. – ACEGL Sep 20 '17 at 22:16
9
import zipfile
import StringIO

text = u"ABCDEFGHIJKLMNOPQRSTUVWXYVabcdefghijklmnopqqstuvweyxáéöüï东 廣 広 广 國 国 国 界"

zipstream=StringIO.StringIO()
file = zipfile.ZipFile(file=zipstream,compression=zipfile.ZIP_DEFLATED,mode="w")
file.writestr("data.txt.zip",text.encode("utf-8"))
file.close()
zipstream.seek(0)
self.response.headers['Content-Type'] ='application/zip'
self.response.headers['Content-Disposition'] = 'attachment; filename="data.txt.zip"'
self.response.out.write(zipstream.getvalue())
2

From What is Google App Engine:

You can upload other third-party libraries with your application, as long as they are implemented in pure Python and do not require any unsupported standard library modules.

So, even if it doesn't exist by default you can (potentially) include it yourself. (I say potentially because I don't know if the Python zip library requires any "unsupported standard library modules".

JMD
  • 7,331
  • 3
  • 29
  • 39