title: NodeJS, Express, how to update client side to display uploaded file
body: <p>I am a beginner when it comes to nodeJS, AJAX requests, and routing. I followed the tutorial here <a href="https://coligo.io/building-ajax-file-uploader-with-node/" rel="nofollow noreferrer">nodejs, express example</a>, and everything is working server-side. I cannot for the life of me however figure out how to display the uploaded file (in this case an image) back to the client. When I make a request for the file which is located in a seperate /uploads/ directory I get a 404 response from the server. I am assuming this is a routing issue, but am confused about how to create the GET request in express for an uploaded file</p>
  
  <h1><strong>app.js</strong></h1>
  
  <pre><code>var express = require('express');
  var app = express();
  var bodyParser = require('body-parser');
  var path = require('path');
  var formidable = require('formidable');
  var fs = require('fs');
  app.use(express.static(path.join(__dirname, 'public')));
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({
    extended: true
  }));
  
  
  //passport for login credentials
  var passport = require('passport');
  var LocalStrategy = require('passport-local').Strategy; //here we implement the strategy that passport uses
  
  var crypto = require('crypto');
  var sqlite3 = require('sqlite3');
  
  var db = new sqlite3.Database('users.sqlite3');
  var check;
  db.serialize(function() {
  
    db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT, salt TEXT)");
    console.log('user table created');
    db.run("DELETE FROM users");    //clear table on run for debug
    var stmt = db.prepare("INSERT INTO users VALUES (?,?,?,?)");
    stmt.run('15', 'tg', 'tg', "333");
    stmt.finalize();
  
    db.each("SELECT username, password, id, salt FROM users", function(err, row) {
        console.log(row.username + ": " + row.password + ": " + row.id + ': ' + row.salt);
    });
  });
  
  //db.close();
  
  
  function hashPassword(password, salt) {
    console.log('password hashing');
    var hash = crypto.createHash('sha256');
    hash.update(password);
    hash.update(salt);
    return hash.digest('hex');
  }
  
  passport.use(new LocalStrategy(function(username, password, done) {
    console.log('using local strat for passport');
    db.get('SELECT salt FROM users WHERE username = ?', username, function(err, row) {
      if (!row) return done(null, false);
      var hash = hashPassword(password, row.salt);
      console.log('done hashing');
      db.get('SELECT username, id FROM users WHERE username = ? AND password = ?', username, hash, function(err, row) {
        if (!row){
          console.log('failure'); 
          return done(null, false);
        }
        console.log('success');
        return done(null, row);
      });
    });
  }));
  
  passport.serializeUser(function(user, done) {
    console.log('serializing');
    return done(null, user.id);
  });
  
  passport.deserializeUser(function(id, done) {
    console.log('deserializing')
    db.get('SELECT id, username FROM users WHERE id = ?', id, function(err, row) {
      if (!row) return done(null, false);
      return done(null, row);
    });
  });
  
  ///end user authentication
  
  //on access to site, serve the user the login page
  app.get('/', function(req, res){
    res.sendFile(path.join(__dirname, 'views/home.html'));
  
  });
  
  //providing routing access for success/failure on login attempt
  app.post('/login', 
    passport.authenticate('local', {  successRedirect: '/home',
                                      failureRedirect: '/fail' }));
  
  app.get('/fail', function(req,res){
    res.sendFile(path.join(__dirname, 'views/error.html'));
  });
  
  app.get('/home', function(req,res){
    res.sendFile(path.join(__dirname, 'views/home.html'));
  });
  
  app.get('/login', function(req,res){
     res.sendFile(path.join(__dirname, 'views/login.html'));
     console.log('login unsuccessful');
  });
  
  app.get('/home', function(request, response) {
          response.render('views/home');
  });
  
  app.get('/upload' , function(req, res){
      res.sendFile(__dirname + '/uploads');
  });
  
  app.post('/upload', function(req, res){
  
    // create an incoming form object
    var form = new formidable.IncomingForm();
  
    // specify that we want to allow the user to upload multiple files in a single request
    form.multiples = true;
  
    // store all uploads in the /uploads directory
    form.uploadDir = path.join(__dirname, '/uploads');
  
    // every time a file has been uploaded successfully,
    // rename it to it's orignal name
    form.on('file', function(field, file) {
      fs.rename(file.path, path.join(form.uploadDir, file.name));
    });
  
    // log any errors that occur
    form.on('error', function(err) {
      console.log('An error has occured: \n' + err);
    });
  
    // once all the files have been uploaded, send a response to the client
    form.on('end', function() {
      res.end('success');
    });
  
    // parse the incoming request containing the form data
    form.parse(req);
  
  });
  
  var server = app.listen(3000, function(){
    console.log('Server listening on port 3000');
  });
  </code></pre>
  
  <h1>upload.js</h1>
  
  <pre><code>'use strict'
  
  $('.upload-btn').on('click', function (){
      $('#upload-input').click();
      $('.progress-bar').text('0%');
      $('.progress-bar').width('0%');
  });
  
  $('#upload-input').on('change', function(){
  
    var files = $(this).get(0).files;
  
    if (files.length &gt; 0){
      // create a FormData object which will be sent as the data payload in the
      // AJAX request
      var formData = new FormData();
  
      // loop through all the selected files and add them to the formData object
      for (var i = 0; i &lt; files.length; i++) {
        var file = files[i];
  
        // add the files to formData object for the data payload
        formData.append('uploads[]', file, file.name);
      }
  
      $.ajax({
        url: '/upload',
        type: 'POST',
        data: formData,
        processData: false,
        contentType: false,
        success: function(data){
          console.log('upload success');
        },
        xhr: function() {
          // create an XMLHttpRequest
          var xhr = new XMLHttpRequest();
  
          // listen to the 'progress' event
          xhr.upload.addEventListener('progress', function(evt) {
  
            if (evt.lengthComputable) {
              // calculate the percentage of upload completed
              var percentComplete = evt.loaded / evt.total;
              percentComplete = parseInt(percentComplete * 100);
  
              // update the Bootstrap progress bar with the new percentage
              $('.progress-bar').text(percentComplete + '%');
              $('.progress-bar').width(percentComplete + '%');
  
              // once the upload reaches 100%, set the progress bar text to done
              if (percentComplete === 100) {
                $('.progress-bar').html('Done');
                console.log(formData);
                showUploadedItem(file.name);
              }
  
  
            }
  
  
          }, false);
  
          return xhr;
        }
      });
  
    }
  });
  
  
  function showUploadedItem (source) {
        var list = document.getElementById("image-list"),
        li   = document.createElement("li"),
        img  = document.createElement("img");
        img.src = source;
        li.appendChild(img);
        list.appendChild(li);
  }
  </code></pre>