gmonk
gmonk

Reputation: 75

How to Create a MongoDB/mongoengine ImageField from POSTed base64-encoded image?

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.

Upvotes: 3

Views: 3154

Answers (1)

andresarenasv
andresarenasv

Reputation: 131

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

Upvotes: 3

Related Questions