Search code examples
google-app-engineblobstore

Store fetched Image to Blobstore


I have an image file extracted from a url by using urlfetch api of python google app engine.

img = urlfetch.fetch(url).content

I want to upload it to the blobstore in order to be able to serve it to the users. Hence, I'll be willing to save the blob_key to the datastore. As given in the documentations of Google App Engine Blobstore, I do not want to make a upload page for the user as this work is to be done on the server side.

What should be the steps to achieve this?


Solution

  • UPDATE With the arrival of app engine 1.9.0 an application can use the default GCS bucket, which provides an already configured bucket with FREE QUOTA!

    More details here

    You can also send a multi-part form to a blobstore upload_url.

    This is my code to upload a blob to the blobstore:

    from __future__ import unicode_literals
    
    import webapp2
    import mimetypes
    import logging
    from google.appengine.ext.ndb import blobstore                                                      
    from google.appengine.ext.webapp import blobstore_handlers
    from google.appengine.api import urlfetch
    from google.appengine.runtime import DeadlineExceededError
    
    CRLF = b'\r\n'
    BOUNDERY = b'--Th15-Is-ThE-BoUnDeRy--'
    
    
    def _encode_multipart(_fields=None, _files=None):
        """ Generator inserts fields and files to create a multipart payload """
    
        for (field_name, field_value) in _fields or []:
            yield b'--' + BOUNDERY + CRLF
            yield b'Content-Disposition: form-data; name="%s"' % str(field_name) + CRLF
            yield CRLF
            yield str(field_value) + CRLF
    
        for (file_name, file_data) in _files or []:
            yield b'--' + BOUNDERY + CRLF
            yield b'Content-Disposition: form-data; name="file"; filename="%s"' \
                  % str(file_name) + CRLF
            yield b'Content-Type: %s' % mimetypes.guess_type(file_name)[0] + CRLF
            yield CRLF
            yield file_data + CRLF
    
        yield b'--%s--' % BOUNDERY + CRLF
    
    
    def create_upload_url(for_url):
    
        return blobstore.create_upload_url(for_url, max_bytes_per_blob=1048576)
    
    
    def _fetch_blob_update(payload):
    
        upload_url = create_upload_url(webapp2.uri_for('blob_update', _full=True))
        try:
            response = urlfetch.fetch(
                url=upload_url,
                payload=payload,
                method=urlfetch.POST,
                deadline=40,
                follow_redirects=False,
                headers={b'Content-Type': str('multipart/form-data; boundary=%s' % BOUNDERY)}
            )
            if response.status_code != 200:
                logging.error('URL : %s fetch ERROR : %d' % (upload_url, response.status_code))
                return None
            else:
                return response.content
        except (urlfetch.DownloadError, DeadlineExceededError), e:
            logging.error('fetch %s download or deadline error, exception : %s'
                          % (upload_url, str(e)))
            return None
    
    
    def blob_update_by_upload(ndb_key, blob):
        """ update blob using upload to the blobstore """
    
        result = _fetch_blob_update(b''.join(_encode_multipart(_files=[(ndb_key.id(), blob)])))
        return blobstore.BlobKey(result)
    
    
    class BlobUpdate(blobstore_handlers.BlobstoreUploadHandler):
        """ has named route : blob_update """
    
        def post(self):
            """ blob upload handler returns the new blobkey """
    
            blob_info = self.get_uploads('file')[0]
            self.response.headers[b'Content-Type'] = b'text/plain'
            self.response.write(str(blob_info.key()))
    

    Route:

    webapp2.Route(r'/blob_update', handler='....BlobUpdate', name='blob_update')