0

I have an image saved on a MongoDB. The model is the following:

picture: {
    metadata: {
        name: { type: String, default: null },
        comment: { type: String, default: null },
        publisherID: { type: String,default: null },
        date: { type: Date, default: Date.now },
        size: { type: Number,default: 0 },
        type: { type: String, default: null }
    },
    data: { type: Buffer, default: null },
    tags: Array
}

Now I need to load the image again from the DB.

I make an AJAX call and request the picture with the id.

        $.ajax({
        type: "POST",
        url: window.location.origin + '/picture',
        contentType: 'application/json',
        dataType: 'json',
        async: true,
        data: JSON.stringify({ id: id }),
        success: function (result) {
            console.log(result);
            a = result;

            var img = result.result[0].picture.data.join("").toString('base64');
            img = "data:" + result.result[0].picture.metadata.type + ";base64," + img;

            $('#img').attr('src', img);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log('error ' + textStatus + " " + errorThrown);
            success = false;
        }
    });

And this is the handler on the server

    var Picture = require('../models/picture');
Picture.find({ "_id": req.body.id}, function (err, pic) {
    if (err || !pic)
        res.end(JSON.stringify({ result: "error" }));

    if (pic) {
        console.log(pic);
        res.end(JSON.stringify({ result: pic }));
    }
})

I have translated the binary data into base64 but the image doesnt display. (I had to join the binary data because they came into an array). There are some other similar posts however they dont have anything that I havent done (I think).

2
  • I am really not fond of this approach. It would be better to have an endpoint route that "appears" to be the url of the image being requested. Then the data is just sent in the response as binary. This respect's the browsers cache rather than always being requested as doing this base64 encoded approach will force. Commented Mar 14, 2014 at 3:51
  • I dont really understand what you mean, could you elaborate a bit more? Commented Mar 14, 2014 at 8:12

1 Answer 1

1

As stated in the comments, it is better to have a separate endpoint in your application to make these calls "look like" standard static file requests. So the first thing I would do is change your schema a little:

picture: {
    metadata: {
        name: { type: String, default: null },
        comment: { type: String, default: null },
        publisherID: { type: String,default: null },
        date: { type: Date, default: Date.now },
        size: { type: Number,default: 0 },
        type: { type: String, default: null }
    },
    path: { type: String, required: true },
    mime: { type: String, required: true },
    data: { type: Buffer, default: null },
    tags: Array
}

So that adds two fields which are going to identify the "path" to the image to match, and "mime" as the mime-type of the file. So "path" is a more "friendly" identifier than an _id and the "mime-type" would be set in insert to match the returned content type.

Then you set up a route to serve the content:

app.get('/images/:imgname', function(req,res) {

    Picture.find({ "picture.path": req.param("imgname") }, function(err,pic) {
        if (err) // checking here

        // Sending response        
        res.set('Content-Type', pic.mime);
        res.send( pic[0].picture.data );
    }); 

})

So when you did a request like:

  wget http://localhost:3000/images/test.png

This would happen:

  • Find the document matcing "path" for "test.png"

  • Assign the document property for "picture.mime" as the Content-Type for the response

  • Send the binary data back as the response

So for the client, it's an actual file as the response, and the point is the that "browser" can cache this and not hit your application where the "cached" copy is valid.

If you are embedding Base64 encoded data in JSON responses then you loose that important part and you send the data every time. It's also a very messy process to handle, as you have discovered.

Sign up to request clarification or add additional context in comments.

3 Comments

really nice man! much appreciated. However I am gonna stick with an ID in order avoid duplicates.
@drakoumelitos or you could make the "freindly" path name subject to a unique index constraint. But the implementation details are up to what best suits you.
true true, but I would prefer to keep as unfriendly as possible :D. Anyway thnx a lot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.