Question

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:

  • Why doesn't the initial code work?
  • How could I return the image back so that it could be used in an image tag?
  • When the image is being returned, what exactly is being returned? Binary data that the browser is interpreting? Something else?
  • And does the image consist of the merged chunks in the data field of the fs.chunks collection documents?
Was it helpful?

Solution

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">.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top