1

Using Python Image Library PIL and Google App Engine Blobstore...

This:

img = images.Image(blob_key=image)
logging.info(img.size)
self.response.headers['Content-Type'] = 'image/jpeg'
self.response.out.write(img)

Has Attribute error:

AttributeError: 'Image' object has no attribute 'size'

So the Image instance from google app engine does not have size?

So then how does this work:

img = images.Image(blob_key=image)
img.resize(width, height)
img.im_feeling_lucky()
thumbnail = img.execute_transforms(output_encoding=images.JPEG)
self.response.headers['Content-Type'] = 'image/jpeg'
self.response.out.write(thumbnail)

What am I missing?

EDIT:

The fix was using the get_serving_url and not use my image server as proposed by @voscausa. Due to the fact that my object was parsed by jinja2 templating it was impossible to create a Image object via jinja2. So the final solution worked as below:

class Mandelbrot(db.Model):
  image = blobstore.BlobReferenceProperty()

@property
def image_url(self):
  return images.get_serving_url(self.image)

This way I could parse the image url to my page like:

<img src=
{% if mandelbrot.image %}
  "{{ mandelbrot.image_url }}" 
{% else %} 
  "./assets/img/preloader.gif"
{% endif %}
/>
2
  • To explain a bit more. The first method without the resize does not serve a valid image resource. That is the issue and not the size. Commented Dec 11, 2012 at 13:39
  • Your question really isn't about PIL, you should change the title. Google might use PIL to implement its Image class but it's a completely separate thing. Commented Dec 11, 2012 at 16:39

2 Answers 2

3

I'am not familiair with PIL, because I use another solution from Google for serving and sizing images. Google can serve the images for you, using Google High Performance Image serving. This means:

  • you have to create once, a serving_url for images in the blobstore using : get_serving_url
  • You can change the size of the served image. The original is not changed
  • Google will serve the images almost for free for you. You do not need a handler. You only pay the bandwidth

Here is an example. You can change the =s0, to change the size. s0 returns the original size.

https://lh6.ggpht.com/1HjICy6ju1e2GIg83L0qdliUBmPHUgKV8FP3QGK8Qf2pHVBfwkpO_V38ifAPm-9m20q_3ueZzdRCYQNyDE3pmA695iaLunjE=s0

get_serving_url docs: https://developers.google.com/appengine/docs/python/images/functions

Code :

class Dynamic(db.Model):          # key : name
    name = db.StringProperty() 
    blob_ref = blobstore.BlobReferenceProperty()
    serving_url = db.LinkProperty()

dyn= Dynamic.get_by_key_name(key_name)
try :       # get url with size = 0
    dyn.serving_url = images.get_serving_url(dyn.blob_ref, size=None, secure_url=True)
except DeadlineExceededError : 
    try :             # sometimes this request fails, retry. This always works fine
        dyn.serving_url = images.get_serving_url(dyn.blob_ref, size=None, secure_url=True)
    except DeadlineExceededError :
        logging.error('Image API get_serving_url deadline error after retry' %(dyn.key().name()))                        
        return None
    dyn.put()
Sign up to request clarification or add additional context in comments.

5 Comments

Agreed that this is the best approach. Thank you for your answer. But a quick question. If you wanted to get the serving url via a template ? eg: object.image = self._save_to_blobstore_and_get_key(image) How could someone when parsing the object to a template retrieve the url without first creating an image object?
I use a datastore entity with references to the blobstore (last uploaded version) and the serving_url. When I create the template I use this entity to fill the template and also add the size (s=xxx) to the template. I did not understand your code, so I have edited my answer to add the code.
Yes your example is correct, though you where faster! Also your example is saving up resources while not always calling the get_serving_url() Also please see my corrected example
Looks OK. You can add the dynamic size (=sxxx) to your template and if size is not defined you can add a default : =s0 Like : {{ mandelbrot.image_url }}=s{{ mandelbrot.size|default('0') }}
just a note : you may also use google cloud storage to store the images and get the serving url not only the blobstore.
1

It looks like the GAE version of PIL doesn't implement .size. Use something like this instead:

logging.info((img.width, img.height))

1 Comment

My issue @Blender is not getting the size but serving the image. Disregard the size. It was just for reference as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.