3

I have a small Python/Flask application that should store a picture in MongoDB.

  1. The a client submits a HTTP POST (JSON) with one of the fields being the base64-encoded image.
  2. The server should store this image in a MongoDB ImageField. I'm using mongoengine right now.

The Model:

class Image(db.EmbeddedDocument):
    data = db.ImageField()

Right now, the relevant server code looks like this:

import Image as PIL
import base64
import cStringIO # I'm trying to give PIL something that can handle common file operations without having to write to disk

imageData = jsondata['image']
file_like = cStringIO.StringIO(base64.decodestring(imageData))
PILImage = PIL.open(file_like)

# new mongo object
img = Image(name="imagename")
img.data = PILImage # assignment of image data
img.save()

This gives me the error #=>ValidationError: ValidationError (Location:53e37ed6844de403e0998182) (image.grid_id: ['images'])

When I change the assignment of the image data to this:

img.data.put(PILImage)

I get the error: #=> ValidationError: Invalid image: read

So I thought it perhaps is looking for an object that supports a 'read' method. When I change the assignment to this:

img.data.put(file_like)

I get the error: #=> "ValidationError: Invalid image: cannot identify image file "

So, I'm able to base64-encode, json.loads(), POST, json.dumps(), base64decode, and create a PIL image from the data, but I somehow can't get the MongoDB ImageField to accept it as an image.

Can anyone help?

One thing: I found that if I simply write the PILImage to disk and then store it by telling mongoengine to

img.data.put("path/to/image/file")

I can work around this, but I would like to avoid filesystem operations, as the application will experience a fair amount of traffic and we suspect IO will be the first bottleneck.

gmonk
  • 75
  • 1
  • 5

1 Answers1

3

If you still need it, here it is my solution:

import tempfile

# this can change depending if you send the JSON by url or not
file_like = base64.b64decode(imageData)
bytes_image = bytearray(file_like)

with tempfile.TemporaryFile() as f:
    f.write(bytes_image)
    f.flush()
    f.seek(0)
    img.data.put(f)

img.save()

Hope it helps

  • This creates an actual (temp)file on the filesystem, right? I was looking to avoid that because of the I/O cost of hundreds of these transactions occurring near-simultaneously. – gmonk Oct 07 '14 at 12:43
  • Yes, that creates an actual temp file on the filesystem. But according to Mongo doc, this is the only way to save an img to and ImageField. A good thing about the temp file is that once you've exit the statement (via normal exit, return, exception, or anything else) the file/directory and it's contents will be removed from the filesystem. If you want to avoid the I/O cost you can try saving the Image in base64 in a StringField. Hope it helps, sorry for responding so late. – andresarenasv Oct 14 '14 at 19:33
  • Thanks for the response. I guess that basically means it's not possible without paying some small cost in I/O. Your answer is as close as it's going to get (tempfile or stringfield). Thanks! – gmonk Oct 17 '14 at 17:00
  • Did this, worked like a charm. Now trying to query the images stored. SO here for it if anybody knows: https://stackoverflow.com/questions/44889557/flask-mongoengine-paginated-documents-with-image-field – David Crook Jul 03 '17 at 15:55