0

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.

BackPacker777
  • 673
  • 2
  • 6
  • 21
  • what does NET tab in browser development window say? is it 404? – danday74 Apr 15 '16 at 17:25
  • Says 'Status 200' for the image file. – BackPacker777 Apr 15 '16 at 17:36
  • ok that means its finding the image file - what is the content type in the response headers? you will find that in the NET tab also, you should be able to expand the request information. I am guessing it is serving it as text/plain but it should be serving it as an image/png or something - what type of image are you serving? PNG? JPG? GIF? (im gonna guess its a GIF) – danday74 Apr 15 '16 at 17:41
  • The content type is jpg. – BackPacker777 Apr 15 '16 at 17:51

2 Answers2

1

It looks like your NODE server is setting the wrong MIME type. You can set the MIME type yourself, as you are doing, but this gets awfully painful. I would recommend using a MIME type node module that is made for this exact purpose.

https://www.npmjs.com/package/mime

This npm package does exactly this with very little effort.

Kendra
  • 769
  • 1
  • 20
  • 34
danday74
  • 52,471
  • 49
  • 232
  • 283
  • I appreciate this, and I will use more libraries as well as the Koa framework, but I first want to learn why this problem is happening... – BackPacker777 Apr 15 '16 at 18:29
  • Hi Dan. I am one of the several editors who have been trimming the chatty and religious material from your posts. We're rather keen on keeping signatures, salutations and unnecessary fluff from posts and answers, and we're now in a position where you've been asked several times to be more succinct, and you're now [ignoring moderator opinion](http://stackoverflow.com/a/36553286/472495) and rolling back helpful edits. Would you be willing to have a dialogue about this in the comments? – halfer Apr 16 '16 at 13:49
  • The bottom line is that you are welcome to put whatever you like in your profile and avatar, but that posts need to keep a focus on the material at hand. This is a Q&A site, not a forum, and everyone can expect their material to be edited (rolling back is permissible, but only if the edit was not constructive). I have previously [raised this issue on Meta](http://meta.stackoverflow.com/q/320874/472495) so our actions and conversation are in public view. – halfer Apr 16 '16 at 13:50
1

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.

x4rf41
  • 5,184
  • 2
  • 22
  • 33
  • I verified that the file is not corrupt. Here are the response headers: HTTP/1.1 200 OK Content-Type: image/jpeg Date: Sat, 16 Apr 2016 10:02:24 GMT Connection: keep-alive Transfer-Encoding: chunked – BackPacker777 Apr 16 '16 at 10:19
  • @BackPacker777 I think I found your mistake. I changed my answer, this should work now. – x4rf41 Apr 16 '16 at 14:56