user1063287
user1063287

Reputation: 10879

How to display an image from MongoDB database via PyMongo and Bottle?

Desired Behaviour

Upload an image to GridFS and then show it in the browser (just to get an idea of how GridFS works).

Current Behaviour

Image is uploaded to GridFS collections (I can access it via the shell) and then a 500 error is returned.

Error

Error: 500 Internal Server Error

Sorry, the requested URL 'https:/mysite.com/form_action_path' caused an error:

Form

<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="data" />
<input type="submit" value="submit">
</form>

Bottle

# relevant libraries
import gridfs
from bottle import response

@route('/upload', method='POST')
def do_upload():
    data = request.files.data
    name, ext = os.path.splitext(data.filename)
    if ext not in ('.png','.jpg','.jpeg'):
        return "File extension not allowed."
    if data and data.file:
        raw = data.file.read()
        filename = data.filename
        dbname = 'grid_files'
        db = connection[dbname]
        fs = gridfs.GridFS(db)
        fs.put(raw,filename=filename)
        # this is the part where I am trying to get the image back out
        collection = db.fs.files
        cursor = collection.find_one({"filename":"download (1).jpg"})
        response.content_type = 'image/jpeg'
        return cursor
    return "You missed a field."

Edit:

This returns an image in the browser:

        # .... same as above
        # this is the part where I am trying to get the image back out
        thing = fs.get_last_version(filename=filename)
        response.content_type = 'image/jpeg'
        return thing

My remaining questions would be:

Upvotes: 2

Views: 3832

Answers (1)

A. Jesse Jiryu Davis
A. Jesse Jiryu Davis

Reputation: 24017

The initial code doesn't work because you're returning a document, which PyMongo represents as a Python dictionary. Flask doesn't know what to do with it. (Note that find_one() returns a single document, and find() returns a Cursor.)

Your final code returns the "thing" it gets from GridFS.get_last_version(), which is a GridOut object for reading from a GridFS file.

GridOut is iterable: iterating a GridOut obtains chunks of byes. Flask does know how to turn an iterable into an HTTP response, so the second version of your code works.

The lesson is: when you want to interact with GridFS, use the GridFS class rather than find() or find_one().

Yes, the image consists of merged chunks data from the fs.chunks collection.

To include the image in an <img> tag, something like this should work:

@route('/images/<filename>')
def image(filename):
    fs = gridfs.GridFS(db)
    gridout = fs.get_last_version(filename=filename)
    response.content_type = 'image/jpeg'
    return gridout

Then in your HTML, <img src="/images/filename.jpg">.

Upvotes: 2

Related Questions