7
votes

I want the user to be able to upload images to Google App Engine. I have the following (Python):

class ImageData(ndb.Model):
     name = ndb.StringProperty(indexed=False)
     image = ndb.BlobProperty()

Information is submitted by the user using a form (HTML):

<form name = "input" action = "/register" method = "post">
    name: <input type = "text" name = "name">
    image: <input type = "file" name = "image">
</form>

Which is then processed by:

class AddProduct(webapp2.RequestHandler):
    def post(self):
        imagedata = ImageData(parent=image_key(image_name))
        imagedata.name = self.request.get('name')
        imagedata.image = self.request.get('image')
        imagedata.put()

However, when I try to upload an image, lets say "Book.png", I get the error: BadValueError: Expected str, got u'Book.png'

Any idea what is going on? I have been working with GAE for quite some time, but this is the first time I had to use blobs.

I used this link: https://developers.google.com/appengine/docs/python/images/usingimages which uses db, not ndb. I also tried storing the image in a variable first like in the link: storedInfo = self.request.get('image') and then storing it: imagedata.image = ndb.Blob(storedInfo) Which ALSO gives me an error: AttributeError: 'module' object has no attribute 'Blob' Thanks in advance.

3
The error is telling you that you're trying to set a Unicode object as the value of a blob, which in this case seems to be the file name, not the file data itself. (I'm not sure how to get the raw data as an str in webapp2, so just posting as a comment) - Wooble
any reason you are not using the blobstore api than datastore? developers.google.com/appengine/docs/python/blobstore/… - Faisal
Thank you Wooble, but what would you suggest I do? And Faisal, I am not using it because it needs db (I am using ndb) and webapp (I am using webapp2). - Albraa
I don't use webapp2 (or webapp for that matter) but a cursory examination of the documentation shows that you are doing it wrong. An uploaded file will normally be accessed through a cgi.FieldStorage obejct - see the docs webapp-improved.appspot.com/guide/request.html . In addition you may need to set the encoding normally to something like multipart/form-data See docs - w3.org/TR/html401/interact/forms.html#h-17.13.4 - Tim Hoffman
Could you put a link to that example so others, who have the same problem, get it done? - doru

3 Answers

9
votes

Had the same prob.

just replace

imagedata.image = self.request.get('image')

with:

imagedata.image = str(self.request.get('image'))

also your form needs to have enctype="multipart/form-data

<form name = "input" action = "/register" method = "post" enctype="multipart/form-data">
5
votes

There is a great example in the documentation that describes how to upload files to the Blobstore using a HTML form: https://developers.google.com/appengine/docs/python/blobstore/#Python_Uploading_a_blob

The form should point to a url generated by blobstore.create_upload_url('/foo') and there should be a subclass of the BlobstoreUploadHandler at /foo like this:

class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
  def post(self):
    upload_files = self.get_uploads('file')
    blob_info = upload_files[0]
    imagedata = ImageData(parent=image_key(image_name))
    imagedata.name = self.request.get('name')
    imagedata.image = blob_info.key()
    imagedata.put()

For this to work, you should change your data model such that in ImageData, image referes to a ndb.BlobKeyProperty().

You can serve your image simply from a url generated by images.get_serving_url(imagedata.image), optionally resized and cropped.

3
votes

You must add enctype="multipart/form-data" to your form in order for this to work

<form name = "input" action = "/register" method = "post" enctype="multipart/form-data">
    name: <input type = "text" name = "name">
    image: <input type = "file" name = "image">
</form>