BackPacker777 BackPacker777 - 5 months ago 11
Node.js Question

Why isn't node serving my image file?

I have a vanilla node.js http server. Everything except my image file works. I just get the broken image icon on the page.

Here is my server code:

"use strict";

class app {
constructor() {
app.loadServer();
}

static loadServer() {
const HTTP = require('http'),
PORT = 1337,
SERVER = HTTP.createServer(function(req, res) {
let httpHandler = function(err, str, contentType) {
console.log('\n\n' + 'Content type: ' + contentType + '\n\n');
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('An error has occurred: ' + err.message);
} else if (contentType.indexOf('image') >= 0) {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str, 'binary');
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str);
}
};

if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
if (req.method == 'POST') {
app.getFormData(req, res);
} else {
console.log("[405] " + req.method + " to " + req.url);
res.writeHead(405, "Method not supported", { 'Content-Type': 'text/html' });
res.end('<html><head><title>405 - Method not supported</title></head><body><h1>Method not supported.</h1></body></html>');
}
} else if (req.url.indexOf('/javascripts/') >= 0) {
app.render(req.url.slice(1), 'application/ecmascript', httpHandler);
} else if (req.url.indexOf('/css/') >= 0) {
app.render(req.url.slice(1), 'text/css', httpHandler);
} else if (req.url.indexOf('/images/') >= 0) {
app.render(req.url.slice(1), 'image/jpg', httpHandler);
} else {
app.render('public/views/index.html', 'text/html', httpHandler);
}

}).listen(PORT, function() {
console.log('-= Francis Server Listening at http://127.0.0.1:' + PORT + ' =-');
});
}

static render(path, contentType, callback) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}

static getFormData(req, res) {
const FORMIDABLE = require('formidable'),
DO_NAMES = require('./node/NameClass');
let formData = {};
new FORMIDABLE.IncomingForm().parse(req)
.on('field', function(field, name) {
formData[field] = name;
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
let finalName = new DO_NAMES(formData);
res.writeHead(200, {'content-type': 'text/plain'});
res.write('-= Received form: ');
res.end(finalName.getFirstName() + ' ' + finalName.getLastName());
});
}
}

module.exports = app;


It feels like it's trying to serve the image as text instead of picture. I verified that the image is there and readable.

Answer

I found the problem.

it happens here:

FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
    callback(err, str, contentType);
});

You read the image file as UTF-8 but it is a binary file. That is why the image data is corrupt. Instead you have to use binary as encoding. You could change your code like this:

static render(path, contentType, callback, encoding) {
    const FS = require('fs');
    FS.readFile(__dirname + '/' + path, encoding ? encoding : 'utf-8', function(err, str) {
        callback(err, str, contentType);
    });
}

and then call render like this:

app.render(req.url.slice(1), 'image/jpeg', httpHandler, 'binary');

There are obviously better ways to do it but this requires a minimum amount of change to your code. Just make sure the readFile() encoding is binary for binary files.

Also the correct mime type for jpg is image/jpeg not image/jpg. Most, if not all, browsers won't care but it is more clean.