views:

36

answers:

1

I've been trying to Base64 encode image data from the user (in this case a trusted admin) in order to skip as many calls to the BlobStore as I possibly can. Every time I attempt to encode it, I recieve an error saying:

Error uploading image: 'ascii' codec can't decode byte 0x89 in position 0: ordinal not in range(128)

I've googled the error (the less significant parts) and found that it may have something to do with Unicode (?). The template portion is just a basic upload form, while the handler contains the following code:

  def post(self,id):
    logging.info("ImagestoreHandler#post %s", self.request.path)
    fileupload = self.request.POST.get("file",None)
    if fileupload is None : return self.error(400)

    content_type = fileupload.type or getContentType( fileupload.filename )
    if content_type is None: 
      self.error(400)
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write( "Unsupported image type: " + fileupload.filename )
      return
    logging.debug( "File upload: %s, mime type: %s", fileupload.filename, content_type )

    try:
      (img_name, img_url) = self._store_image(
        fileupload.filename, fileupload.file, content_type )
      self.response.headers['Location'] = img_url
      ex=None
    except Exception, err:
      logging.exception( "Error while storing image" )
      self.error(400)
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write("Error uploading image: " + str(err))
      return
    #self.redirect(urlBase % img.key() )  #dummy redirect is acceptable for non-AJAX clients,
    # location header should be acceptable for true REST clients, however AJAX requests 
    # might not be able to access the location header so we'll write a 200 response with 
    # the new URL in the response body:

    acceptType = self.request.accept.best_match( listRenderers.keys() )
    out = self.response.out
    if acceptType == 'application/json':
      self.response.headers['Content-Type'] = 'application/json'
      out.write( '{"name":"%s","href":"%s"}' % ( img_name, img_url ) )
    elif re.search( 'html|xml', acceptType ):
      self.response.headers['Content-Type'] = 'text/html'
      out.write( '<a href="%s">%s</a>' % ( img_url, img_name) )

  def _store_image(self, name, file, content_type):
    """POST handler delegates to this method for actual image storage; as
    a result, alternate implementation may easily override the storage
    mechanism without rewriting the same content-type handling. 

    This method returns a tuple of file name and image URL."""

    img_enc = base64.b64encode(file.read())
    img_enc_struct = "data:%s;base64,%s" % (content_type, img_enc)

    img = Image( name=name, data=img_enc_struct )
    img.put()
    logging.info("Saved image to key %s", img.key() ) 
    return ( str(img.name), img.key() )

My image model:

from google.appengine.ext import db

class Image(db.Model):

  name = db.StringProperty(required=True)
  data = db.TextProperty(required=True)
  created = db.DateTimeProperty(auto_now_add=True)
  owner = db.UserProperty(auto_current_user_add=True)

Any help is greatly appreciated. This code, minus my image encoding in _store_image, comes from the blooger branch of gvdent here.

+3  A: 

your store image code could be like this....

img = Image( name=name, data=file.read() )
img.put()
return ( str(img.name), img.key() )

doing base64encode of binary data may increase the size of data itself and increase the cpu encoding and decoding time.

and Blobstore uses the same storage sturcuture as datastore, so it is just making easier to use file upload store download.

iamgopal