views:

1119

answers:

3

I'm trying to upload and save a resized image in a db.BlobProperty field on Google App Engine using Django.

the relevant part of my view that process the request looks like this:

image = images.resize(request.POST.get('image'), 100, 100)
recipe.large_image = db.Blob(image)
recipe.put()

Which seems like it would be the logical django equivalent of the example in the docs:

from google.appengine.api import images

class Guestbook(webapp.RequestHandler):
  def post(self):
    greeting = Greeting()
    if users.get_current_user():
      greeting.author = users.get_current_user()
    greeting.content = self.request.get("content")
    avatar = images.resize(self.request.get("img"), 32, 32)
    greeting.avatar = db.Blob(avatar)
    greeting.put()
    self.redirect('/')

(source: http://code.google.com/appengine/docs/python/images/usingimages.html#Transform)

But, I keep getting an error that says: NotImageError / Empty image data.

and refers to this line:

image = images.resize(request.POST.get('image'), 100, 100)

I'm having trouble getting to the image data. Seems like it's not being uploaded but I can't figure out why. My form has the enctype="multipart/form-data" and all that. I think something's wrong with how I'm referring to the image data. "request.POST.get('image')" but I can't figure out how else to reference it. Any ideas?

Thanks in advance.

+2  A: 

You access uploaded data via request.FILES['field_name'].

http://docs.djangoproject.com/en/dev/topics/http/file-uploads/

hcalves
Thanks for your help hcalves, I must be doing something wrong still because I changed is as follows: image = images.resize(request.FILES['image'], 300, 200) recipe.large_image = db.Blob(image) recipe.put() Now I'm getting a BadImageError on the resize() call. Also, GAE uses django .96 by default... maybe I should try to change that?? I also tried doing request.FILES['image'].read() but that didn't work. It tells me that "'dict' object has no attribute 'read'"I'll keep messing with this tomorrow. But if you've got any more suggestions I'm all ears. Thanks again.
Henrik Joreteg
A: 

Reading more about Google's Image API, seems to me you should be doing something like this:

from google.appengine.api import images

image = Image(request.FILES['image'].read())
image = image.resize(100, 100)
recipe.large_image = db.Blob(image)
recipe.put()

request.FILES['image'].read() should work because it's supposed to be a Django's UploadedFile instance.

hcalves
Thanks hcalves... it was thanks to your help I figured it out. The code above didn't quite work, but got me going in the right direction. Apparently if you generate an image object it and apply transformations to it such as resizing etc. you have to call execute_transforms() on it first otherwise it fails.
Henrik Joreteg
+3  A: 

After some guidance from "hcalves" I figured out the problem. First of all, the default version of Django that comes bundled with App Engine is version 0.96 and how the framework handles uploaded files has changed since then. However in order to maintain compatibility with older apps you have to explicitly tell App Engine to use Django 1.1 like this:

from google.appengine.dist import use_library
use_library('django', '1.1')

You can read more about that in the app engine docs.

Ok, so here's the solution:

from google.appengine.api import images

image = request.FILES['large_image'].read()
recipe.large_image = db.Blob(images.resize(image, 480))
recipe.put()

Then, to serve the dynamic images back again from the datastore, build a handler for images like this:

from django.http import HttpResponse, HttpResponseRedirect

def recipe_image(request,key_name):
    recipe = Recipe.get_by_key_name(key_name)

    if recipe.large_image:
        image = recipe.large_image
    else:
        return HttpResponseRedirect("/static/image_not_found.png")

    #build your response
    response = HttpResponse(image)
    # set the content type to png because that's what the Google images api 
    # stores modified images as by default
    response['Content-Type'] = 'image/png'
    # set some reasonable cache headers unless you want the image pulled on every request
    response['Cache-Control'] = 'max-age=7200'
    return response
Henrik Joreteg