7

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.

doru
  • 9,022
  • 2
  • 33
  • 43
Albraa
  • 337
  • 1
  • 4
  • 11
  • 2
    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 Aug 23 '13 at 01:58
  • 1
    any reason you are not using the blobstore api than datastore? https://developers.google.com/appengine/docs/python/blobstore/#Python_Uploading_a_blob – Faisal Aug 23 '13 at 02:11
  • 1
    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 Aug 23 '13 at 03:11
  • 2
    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 http://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 - http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 – Tim Hoffman Aug 23 '13 at 09:56
  • Thanks a lot for the links guys. Finally figured it out, I found an example about using blobs and studied how it works, then incorporated that into my code. Again, thanks everyone. – Albraa Aug 24 '13 at 01:12
  • 2
    Could you put a link to that example so others, who have the same problem, get it done? – doru Sep 23 '13 at 10:50

3 Answers3

9

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">
Orane
  • 2,223
  • 1
  • 20
  • 33
5

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.

pfalke
  • 336
  • 1
  • 9
  • I think this only works with python 2.7 - I'm now looking for a python 3.7 equivalent. It seems straightforward to get a browser to post to app engine then write that to cloud storage, but being able to get the user to post directly to cloud storage isn't something I've found documentation for as yet. – Mark May 29 '19 at 04:26
3

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>
Steve
  • 365
  • 5
  • 10
user2764108
  • 146
  • 2
  • 10