I went through Manuel Kiessling’s awesome Node.js tutorial. It covered some fundamentals for beginner to advanced level programmers that are new to Node.js.

Manuel covered uploading image files to a server and displaying it on the client’s browser (by streaming it down).

The tutorial was really simple. It was also a static solution. The application displays an upload screen, and expects a .png image. The image gets renamed to a static name (test.png) after it’s uploaded. When viewing the image under the path /show, the application streams down an image. The image path is also hard-coded in the application.

However, as a developer, things on the web are dynamic rather than static. So I decided to take Manuel’s tutorial one step further,

I wanted the application to upload an image and save it as it’s original file name. I also wanted to stream down the image based on a querystring parameter, making viewing the image a bit more dynamic.

In order to do this, I modified the show and upload functions in the requestHandlers.js module. I used the querystring, url and mime modules.

You can include mime by using node’s package manger:

npm install mime  

Newly included libraries

var requestHandler      = {},  
    url                 = require( "url" ),
    qs                  = require( "querystring" ),
    fs                  = require( "fs" ),
    formidable          = require( "formidable" ),
    mime                = require( "mime" ) /* Used to lookup mime type */
;

Updates to upload

requestHandler.upload = function( response, request ) {

    console.log( "Request for 'upload' is called." );

    var form = new formidable.IncomingForm();
    console.log( "Preparing upload" );

    form.parse( request, function( error, fields, files ) {
        console.log( "Completed Parsing" );

        if( error ){
            response.writeHead( 500, { "Content-Type" : "text/plain" } );
            response.end( "CRAP! " + error + "\n" );
            return;
        }

    fs.renameSync( files.upload.path, "/tmp/" + files.upload.name ); // Update the streamed filename with it's original filename

    response.writeHead( 200, { "Content-Type" : "text/html" } );

    response.write( "received image" );

    response.end( "<img src='/show?i=" + files.upload.name + "' alt='' />" ); // Display the file using the query variable (i)

    });
};

Straight forward. I updated the fileSystem’s renameSync method to use the file name instead of statically setting it to /tmp/test.png. I wanted to upload any type of file, not just .png’s.

Updates to show

requestHandler.show = function( response, request ) {  
    console.log( "Request handler 'show' was called. " );
    var image = qs.parse( url.parse( request.url ).query ).i; // Parse out the query string variable

    if( !image ) { // Make sure we have a value
        response.writeHead( 500, { "Content-Type" : "text/plain" } );
        response.end( " No Image in QueryString ");
        return;
    }
    fs.readFile( "/tmp/" + image, "binary", function( error, file ) { // Read the file
        var type = ""; // Mime-Type valueif( error ){
        response.writeHead( 500, { "Content-Type" : "text/plain" } );
        response.end( error + "\n" );
        return;
}

type = mime.lookup( file ); // Lookup the files mime-type

response.writeHead( 200, { "Content-Type" : type } ); // Pass in the mime-type  
response.end( file, "binary" ); //Stream the file down

});
};

In this example, I included the request parameter. I used the request parameter to parse out the querystring i variable, wich contained the image name we're going to stream down. I also made use of the mime module to verify the file's mime-type, this way I can set the Content-Type to any type of image, not just .png's.
Now I can point to http://localhost:8080/show?i=image_name.[png,gif,jpg,bmp] and will stream down any image contained in /tmp

The final code

var requestHandler  = {},  
    url             = require( "url" ),
    qs              = require( "querystring" ),
    fs              = require( "fs" ),
    formidable      = require( "formidable" ),
    mime            = require( "mime" )
;


requestHandler.start = function( response ) {  
    var body = "<html>\n"+
               "<head>\n"+
                "\t<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n"+
                "\t<title>Welcome to the nodejs tutorial</title>\n"+
                "</head>\n"+
                "<body>\n"+
                "\t<form action="/upload" method="post" enctype="multipart/form-data">\n" +
                "\t\t<input type="file" name="upload" />\n" +
                "\t\t<input type="submit" value="Upload File" />\n" +
                "\t</form>\n"+
                "</body>\n"+
                "</html>\n";

    console.log( "Request for 'start' is called." );

    response.writeHead( 200, { "Content-Type" : "text/html" } );
    response.end( body );
};

requestHandler.upload = function( response, request ) {  
    console.log( "Request for 'upload' is called." );

    var form    = new formidable.IncomingForm();

    console.log( "Preparing upload" );


    form.parse( request, function( error, fields, files ){
        console.log( "Completed Parsing" );

        if( error ){
            response.writeHead( 500, { "Content-Type" : "text/plain" } );
            response.end( "CRAP! " + error + "\n" );
            return;
        }

        fs.renameSync( files.upload.path, "/tmp/" + files.upload.name );

        response.writeHead( 200, { "Content-Type" : "text/html" } );
        response.write( "received image <br />" );
        response.end( "<img src='/show?i=" + files.upload.name + "' />" );

    });
};

requestHandler.show = function( response, request ) {  
    console.log( "Request handler 'show' was called. " );

    var image = qs.parse( url.parse( request.url ).query ).i;

    if( !image ){
        response.writeHead( 500, { "Content-Type" : "text/plain" } );
        response.end( " No Image in QueryString ");
        return;
    }

    fs.readFile( "/tmp/" + image, "binary", function( error, file ) {
        var type = "";

        if( error ){
            response.writeHead( 500, { "Content-Type" : "text/plain" } );
            response.end( error + "\n" );
            return;
         }

        type = mime.lookup( file );

        response.writeHead( 200, { "Content-Type" : type } );
        response.end( file, "binary" );

    });
};


exports.start   = requestHandler.start;  
exports.upload  = requestHandler.upload;  
exports.show    = requestHandler.show;