Send array of photos as JSON from NodeJS

Longtime listener, first time caller :-)

I'm new to nodejs and to javascript and am struggling to wrap my head around the event & callback model.

My goal: return array of photos (name, width, height, exif data) as JSON. This is needed so that my app can then work its magic with the JSON array of pics.

Situation: my code seems to work except that things are not running the in the order that I'd like. I'm expecting to stream the JSON for each picture to the HTTP Response but my console logging reveals that things aren't in desired sequence and I suspect that res.end() is being called too early. This means that my web request gets nothing in the response.

The relevant code:

fs.readdir(readDir, function (err, files) {
    var picsInfo;
    if (err) res.writeHead(500, err.message);
    else if (!files.length) res.writeHead(404);
    else {
        res.writeHead(200, { 'Content-Type': 'application/json' });
            files.forEach(function(file) {
        console.log('easyimage');
        easyimg.info(readDir + '/' + file, function(err, stdout, stderr) {
            if (err) throw err;
            console.log(JSON.stringify(stdout));
            res.write(JSON.stringify(stdout));
        });
        });
    }
    res.end();
});

The output I see in my console reveals things are not in order (I'm expecting each photo to be sent to res):

easyimage
easyimage
easyimage
easyimage
easyimage
easyimage
easyimage
easyimage
easyimage
easyimage  
{"type":"JPEG","depth":"8","width":"933","height":"1400","size":"532365B","name":"6.jpg"}
{"type":"JPEG","depth":"8","width":"1400","height":"933","size":"318134B","name":"3.jpg"}
{"type":"JPEG","depth":"8","width":"1400","height":"933","size":"310927B","name":"10.jpg"}
{"type":"JPEG","depth":"8","width":"933","height":"1400","size":"258928B","name":"1.jpg"}
req.method=GET...
{"type":"JPEG","depth":"8","width":"1400","height":"928","size":"384475B","name":"7.jpg"}
{"type":"JPEG","depth":"8","width":"933","height":"1400","size":"469711B","name":"4.jpg"}
{"type":"JPEG","depth":"8","width":"1400","height":"933","size":"392666B","name":"2.jpg"}
{"type":"JPEG","depth":"8","width":"1400","height":"933","size":"354468B","name":"5.jpg"}
{"type":"JPEG","depth":"8","width":"1400","height":"933","size":"438143B","name":"9.jpg"}
{"type":"JPEG","depth":"8","width":"933","height":"1400","size":"304939B","name":"8.jpg"}

I've tried using some examples I've found (e.g. http://blog.nakedjavascript.com/going-evented-with-nodejs but think that I'm just missing something key in terms of the eventing/callback model. Any pointers or help would be much appreciated. Also, if my approach here (i.e. sending JSON to res, etc.) is dumb or sub-optimal I'd also love some tips on what might be best.

Thanks in advance for any pointers & help!

OK, I can't run the code, so it might need some tweaks:

fs.readdir(readDir, function(err, files) {
    var filesPath = files.map(function(file) {
      return readDir + '/' + file;
    });

    async.map(filesPath, easyimg.info, function(err, json) {
        if(err)
            return res.end(500, err.message);

        res.end(JSON.stringify(json));
    });
});

What was wrong with your code ?

  • As I said earlier, res.end() was called immediately because easyimg.info answers asynchronously.
  • You call easyimg.info for each file, but there is no guarantee that the response will arrive in order. See the wonderful async lib (npm install async).
  • When you return an error with a callback, make sure you return as well. Always use the pattern return cb(err).