In Part I of the look at the 7.0.0 release of gadget-ui, I discussed some of the new features of the library and provided some insight into the new NodeJS-based test system.  I left out the discussion of the new FileUploader example written in NodeJS. In short, the new NodeJS test system includes code to demonstrate the FileUploader functionality using NodeJS as a backend. While it isn't necessarily a production-ready solution, it should give you enough code to implement the FileUploader against a NodeJS server without any significant obstacles. Note that this implementation is basically a re-write of the CFML example using NodeJS.

Let's dig right into the example at /test/fileuploader.htm. I have removed the bits of the page that deal with the CFML-based example for the sake of brevity. Here is the JavaScript code:

import {fileuploader,constructor} from '/dist/gadget-ui.es6.js';

var showFile = function( fileItem, div ){
  var dsp = document.querySelector( "#display" );
  dsp.innerHTML = '';
  var img = document.createElement( "img" );
  img.style.width = '500px';
  img.src = "/test/upload/" + fileItem.filename;
  dsp.appendChild( img );
};

var options2 = {
  uploadURI: "/upload",
  tags: "file upload",
  willGenerateThumbnails: true,
  title: "Upload Files",
  showUploadButton: false,
  onUploadComplete: showFile
};

var filedialog2 = constructor( fileuploader, [ document.querySelector("#fileUploadDiv2"), options2 ]);

 

The example uses ES6 module imports, which simplifies the process of loading components from the gadget-ui library. Let's look at the uploadURI in the options2 variable, pointing to /upload. In the root of the gadget-ui project, the index.js file holds the entry point of the test app. Let's focus on the code that handles the /upload URI:

 

let options = {
  inflate: true,
  limit: '1024kb',
  type: 'application/octet-stream'
};
app.use(bodyParser.raw(options));

app.post( "/upload", function( req, res){
    let args = {
      id: req.get("x-id"),
      filename: req.get( "x-filename" ),
      filesize: parseInt( req.get( "x-filesize" ), 10 ),
      part: parseInt( req.get( "x-filepart" ), 10 ),
      parts: parseInt( req.get( "x-parts" ), 10 ),
      contentlength: parseInt( req.get( "Content-Length" ), 10),
      mimetype: req.get( "x-mimetype" )
    };

    if( args.part === 1 ){
      args.id = cuid();
    }

    let tempFile = cuid();
    let wstream = fs.createWriteStream( tempPath + tempFile );
    wstream.write( req.body );
    wstream.end();

    fileService.upload( args.id, (tempPath + tempFile), args.part, args.parts, args.filename, args.filesize )
      .then( function( file ){
        if( args.part === args.parts ){
          res.send( JSON.stringify( file ) );
        }else{
          res.set("x-Id", args.id );
          res.end("");
        }
      })
      .catch( function( error ){
        res.set( "Status-Code", 500 );
        res.send( error );
      });

});

The code uses the body-parser package to parse the code as raw data. Notice in the options that the limit is set at 1024KB, which is the chunk size that the FileUploader component uses. Parsing the request body as raw results in the request body containing a Buffer holding the file contents being uploaded. Since the file is chunked into 1024KB parts, files more than 1024KB in size are uploaded in mutiple parts. Each part is copied into a temporary file on the file system, and a database entry is created for that file part. The example uses MariaDB to hold these entries in the filepart table, but you can easily use any other persistence store to store them.

Next, look at the args collection, which contains the metadata for the file being uploaded. As you can see, the metadata (including the filename) is passed in HTTP headers. We then use fs.createWriteStream() to write the Buffer into a file on the filesystem.

The last section of the code calls the fileservice component (in /model/fileservice.js) to handle the upload. The fileservice component has a couple of basic jobs:

  1. communicate with the filepartdao component to insert, read, and delete the temporary file part entries in the database
  2. when the upload is complete, concatenate the file parts stored on the filesystem into the original file. At this point, your specific solution may call for doing something else with the file. For our purposes, we're just moving it into the /test/upload folder from /test/upload/temp. 

The other interesting aspect of this code is the process of concatenating the file parts using the concat-files package. Since the process is asynchronous, the code returns a Promise (at the top of the upload method) that can be resolved or rejected when the concatenation process succeeds or fails. ( Note that we don't need to wait for the deleteFiles code to finish before we return the result of the upload process. )

That's pretty much it for the FileUploader example. Again, I do not consider this code production-ready, but it should give you enough of an understanding of how to implement the FileUploader against a NodeJS backend.