Example: Uploading Files with QEWD.js

328 views
Skip to first unread message

rtweed

unread,
Aug 6, 2018, 12:56:42 PM8/6/18
to Enterprise Web Developer Community
A pretty common request these days is how to upload files when using QEWD.js.  It's made all the more complex if you want to add security, so that only an authenticated / logged-in user can upload files.

I'm going to provide a pretty-much fully-worked out solution.  I'm going to base it around a front-end built using Vue.js and axiom (the latter a very common module for Ajax and file uploading).  However, with a bit of adaptation, everything I'll demonstrate can be performed with any other framework.

So let's start with the front-end.  I've just taken Ward De Backer's single page Vue.js example, and have added buttons for single and multiple-file uploads.  There's no Login step for this application, to keep it pretty straightforward.

I'm also using Ward's Vue.js plug-in for QEWD: https://github.com/wdbacker/vue-qewd

So here's the main.js file - it's unchanged from Ward's example, except I've changed the url property for the QEWD server - change this appropriately for your QEWD instance..

I've also registered it as an application named "vue-test"


import Vue from 'vue'
import App from './App.vue'

// import both the QEWD class and VueQEWD plugin

import { QEWD, VueQEWD } from 'vue-qewd'

// instantiate QEWD with your parameters

var qewd = QEWD({
  application
: 'vue-test', // <========
  log
: true,
  url
: 'http://192.168.1.135:8080' // <=========
})

// let Vue know you want to use the plugin

Vue.use(VueQEWD, { qewd })

// create your Vue instance

new Vue({
  el
: '#app',
  render
: h => h(App)
})




So now here's the App.vue file, containing the page's UI code:

<template>
 
<div id="app">
   
<template v-if="qewdReady">

     
<img src="./assets/logo.png">
     
<h1>{{ msg }}</h1>
     
<button @click="testing">QEWD message test</button>

     
<div class="container">
       
<div class="large-12 medium-12 small-12 cell">
         
<label>File
           
<input type="file" id="singlefile" ref="file" v-on:change="handleFileUpload()"/>
         
</label>
         
<button v-on:click="submitFile()">Submit</button>
       
</div>
     
</div>

     
<div class="container">
       
<div class="large-12 medium-12 small-12 cell">
         
<label>Files
           
<input type="file" id="multifile" ref="files" multiple v-on:change="handleFilesUpload()"/>
         
</label>
         
<button v-on:click="submitFiles()">Submit</button>
       
</div>
     
</div>

   
</template>

   
<template v-else>
     
<img src="./assets/logo.png">
     
<h2>Starting application, please wait ...</h2>
   
</template>
 
</div>
</template>

<script>

import axios from 'axios'

function getCookie(name) {
 
var value = "; " + document.cookie;
 
var parts = value.split("; " + name + "=");
 
if (parts.length == 2) return parts.pop().split(";").shift();
 
return '';
}

export default {
  name
: 'app',
  created
: function () {
   
var self = this
   
// monitor when QEWD is ready
   
this.$qewd.vRegistrationCallback(function (registered) {
     
// save QEWD Session token as cookie
      self
.$qewd.setCookie('qewd');
      self
.qewdReady = registered //
   
})

   
// start the QEWD WebSockets connection ...

   
this.$qewd.vstart()
 
},

  data
() {
   
return {
      qewdReady
: false,
      msg
: 'Welcome to Your Vue.js App',
      file
: '',
      files
: ''
   
}
 
},

  methods
: {
    testing
: function () {
      let messageObj
= {
        type
: 'test',
        params
: {
          text
: 'a Vue.js test message for QEWD'
       
}
     
};

      let self
= this;
     
this.$qewd.send(messageObj, function(messageObj) {
        self
.msg = messageObj.message.text;
     
});
   
},

   
/*
        Submits the file to the server
    */


    submitFile
(){
       
/*
           Initialize the form data
        */


      console
.log('submitfile!');
      let formData
= new FormData();
           
/*
                Add the form data we need to submit
            */

      formData
.append('singlefile', this.file);
       
/*
          Make the request to the POST /single-file URL
        */

      console
.log('axios post');
      axios
.post( '/upload',
        formData
,
       
{
          headers
: {
           
'Content-Type': 'multipart/form-data',
           
Authorization: 'Bearer ' + getCookie('qewd')
         
}
       
}
     
).then(function(){
        console
.log('SUCCESS!!');
     
})
     
.catch(function(){
        console
.log('FAILURE!!');
     
});
   
},

     
/*
        Handles a change on the file upload
      */



    handleFileUpload
(){
     
this.file = this.$refs.file.files[0];
   
},

     
/*
        Submits multiple files to the server
      */


    submitFiles
(){
       
/*
          Initialize the form data
        */

      let formData
= new FormData();
       
/*
          Iteate over any file sent over appending the files
          to the form data.
        */

     
for( var i = 0; i < this.files.length; i++ ){
        let file
= this.files[i];
        formData
.append('multifile', file);
     
}
       
/*
          Make the request to the POST /multiple-files URL
        */

      axios
.post( '/multiple-files',
        formData
,
       
{
          headers
: {
           
'Content-Type': 'multipart/form-data',
           
Authorization: 'Bearer ' + getCookie('qewd')
         
}
       
}
     
).then(function(){
        console
.log('SUCCESS!!');
     
})
     
.catch(function(){
        console
.log('FAILURE!!');
     
});
   
},
     
/*
        Handles a change on the file upload
      */

    handleFilesUpload
(){
     
this.files = this.$refs.files.files;
   
}
 
}
}

</script>

<style>

#app {
  font
-family: 'Avenir', Helvetica, Arial, sans-serif;
 
-webkit-font-smoothing: antialiased;
 
-moz-osx-font-smoothing: grayscale;
  text
-align: center;
  color
: #2c3e50;
  margin
-top: 60px;
}
h1
, h2 {
  font
-weight: normal;
}
ul
{
  list
-style-type: none;
  padding
: 0;
}

li
{
  display
: inline-block;
  margin
: 0 10px;
}

a
{
  color
: #42b983;
}
</style>



Let me point out the key bits in this file to take note of.  


First, when the application registers itself against the QEWD server, it saves the QEWD Session Token as a cookie:


    this.$qewd.vRegistrationCallback(function (registered) {
      
// save QEWD Session token as cookie
      self
.$qewd.setCookie('qewd'); // <============= 
      self
.qewdReady = registered 
    
})


You'll see why we did this later.



Next, the input fields and associated buttons for uploading a single file:


          <label>File
            
<input type="file" id="singlefile" ref="file" v-on:change="handleFileUpload()"/>
          
</label>
          
<button v-on:click="submitFile()">Submit</button>


and multiple files:


          <label>Files
            
<input type="file" id="multifile" ref="files" multiple v-on:change="handleFilesUpload()"/>
          
</label>
          
<button v-on:click="submitFiles()">Submit</button>



The buttons invoke the functions submitFile() and submitFiles() respectively, which, in turn, invoke the axios method for uploading the files, eg:



    submitFile(){
        
/*
           Initialize the form data
        */


      console
.log('submitfile!');
      let formData 
= new FormData();
            
/*
                Add the form data we need to submit
            */

      formData
.append('singlefile', this.file);
        
/*
          Make the request to the POST /single-file URL
        */

      console
.log('axios post');
      axios
.post( '/upload',
        formData
,
        
{
          headers
: {
            
'Content-Type': 'multipart/form-data',
            
Authorization: 'Bearer ' + getCookie('qewd')
          
}
        
}
      
).then(function(){
        console
.log('SUCCESS!!');
      
})
      
.catch(function(){
        console
.log('FAILURE!!');
      
});
    
},



Notice that I'm sending the QEWD Session Token cookie with the request, in the Authorization header:



            Authorization: 'Bearer ' + getCookie('qewd')



This allows me to verify the authenticity of the user / browser who is trying to upload files, and, if necessary, prevent them from doing so.



That's basically it for the front-end - pretty straightforward stuff.



So now let's look at the QEWD back-end.




By default, QEWD.js uses the standard Node.js Express web server, and that's what I'll use here too.  One thing that Express doesn't provide for you is APIs for file uploading, but one of the most common modules you can use to provide such APIs is "multer"  ( https://www.npmjs.com/package/multer )


Multer is an Express middleware, which means that we much add it within our QEWD's master process, which is where all its activities take place.  This has a couple of consequences:


- when developing and debugging your Multer upload logic, you'll need to stop and restart the master process to get any changes to take effect - stopping the child / worker processes won't do any good!


- when it comes to validating the Authorization header that contains the QEWD Session Token, the master process doesn't have the information needed.  We'll have to engineer a way of passing it to a worker process and perform the validation there: the worker processes have access to the QEWD Session storage.  The Master process doesn't.


We need to add the Multer file upload logic into our QEWD startup file.  Here's my simple example:




var config = {
  managementPassword
: 'keepThisSecret!',
  serverName
: 'QEWD Server',
  port
: 8080,
  poolSize
: 2,
  database
: {
    type
: 'gtm'
 
}
};

config
.addMiddleware = function(bodyParser, app, q) {
 
var multer = require('multer');
 
var fs = require('fs');
 
var dest_path = '/home/rtweed/qewd';
 
var upload = multer({
    dest
: dest_path
 
});

 
function authenticate(req, res, next) {
    console
.log('*** authenticate *** ');
   
// first we need to make sure the request is authorised
   
// via the authorization header which should contain the QEWD session token

   
var error;
   
var authorization = req.headers.authorization;
    console
.log('** checking authorization: ' + authorization);
   
if (!authorization || authorization === '') {
      error
= {error: 'Empty or missing authorization header'};
      res
.status(400);
      res
.send(error);
     
return;
   
}
   
if (authorization.indexOf('Bearer ') === -1) {
      error
= {error: 'Invalid authorization header'};
      res
.status(400);
      res
.send(error);
     
return;
   
}

   
var token = authorization.split('Bearer ')[1];
   
if (token === '') {
      error
= {error: 'Empty bearer token'};
      res
.status(400);
      res
.send(error);
     
return;
   
}

   
// A token was found in the authorization header
   
// send a message to vue-test worker process application handler to check validity of token

   
var messageObj = {
      type
: 'authenticate',
      application
: 'vue-test',
      token
: token
   
};
    q
.handleMessage(messageObj, function(response) {
     
if (response.message.error) {
        res
.status(400);
        res
.send(response.message.error);
       
return;
     
}
     
// token was valid so Ok to upload file
      console
.log('** authorised ok!');
     
next();
   
});
 
}

 
function uploadFile(req, res, next) {
   
// token was valid so Ok to upload file
   
var uploadSingle = upload.single('singlefile');
    uploadSingle
(req, res, function (err) {
     
if (err) {
       
// something went wrong with the upload
        res
.status(400);
        res
.send(response.message.error);
       
return;
     
}

     
// rename the temporary uploaded file using the original file name
     
var target_path = dest_path + '/' + req.file.originalname;
      fs
.rename(req.file.path, target_path);
      res
.send('Successfully uploaded!');
   
});
 
}

 
function uploadFiles(req, res, next) {
    console
.log('** uploadFiles **');
   
var uploadArray = upload.array('multifile', 5);
    uploadArray
(req, res, function (err) {
     
if (err) {
       
// something went wrong with the upload
        res
.status(400);
        res
.send(response.message.error);
       
return;
     
}
      console
.log('*** files uploaded: ' + JSON.stringify(req.files, null, 2));
      req
.files.forEach(function(file) {
       
// rename each temporary file
       
var target_path = dest_path + '/' + file.originalname;
        fs
.rename(file.path, target_path);
     
});
      res
.send('Successfully uploaded!');
   
});
 
}

  app
.post('/upload', [authenticate, uploadFile]);
  app
.post('/multiple-files', [authenticate, uploadFiles]);
};

var qewd = require('qewd').master;
qewd
.start(config);



This looks a bit daunting, but it's pretty straightforward really.  Let's go through the key bits:


I've added the Multer code to the QEWD Master process by using the configuration object's addMiddleWare() function:


      config.addMiddleware = function(bodyParser, app, q) {...}



Note that third argument which gives us access to the ewd-qoper8 / QEWD APIs.  We'll be needing that!



Inside this function we initialise Multer:


  var multer = require('multer');
  
var dest_path = '/home/rtweed/qewd';
  
var upload = multer({
    dest
: dest_path
  
});



Change the dest_path to the one where you want any files uploaded



Jump towards the end of the startup file and you'll see where I define the routes for handling single and multiple file uploads:


     app.post('/upload', [authenticate, uploadFile]);
     app
.post('/multiple-files', [authenticate, uploadFiles]);



These match the axios code in the front-end, eg:


      axios.post( '/upload',


      axios.post( '/multiple-files',



You'll notice that both routes perform a chain of two middleware functions, the first of which authenticates the request.  The trick is that the second one, which does the actual file upload, will only get invoked if the authentication was OK/


Let's see how this works.


The authenticate() function extracts the QEWD Session token from the Authorization header.  If the header doesn't exist, or the token is invalid for whatever reason, an error (Status code 400) is returned to the browser, and the processing is aborted - any files selected by the user will be ignored!


The next step is to validate the QEWD Session token.  This has to be done in a Worker process, but we're currently in the QEWD Master process.  So what we can do is manually send a message to a Worker.  The trick is to create a message that will access the same QEWD application handler module as normal web-socket messages sent from the browser.  This is what this code is doing:



    var messageObj = {
      type
: 'authenticate',
      application
: 'vue-test',
      token
: token
    
};
    q
.handleMessage(messageObj, function(response) { ... }


This will send a message to an available Worker process and invoke the authenticate() message handler function for the "vue-test" application.


Remember right at the start of this posting I noted that the application was registered as an application named "vue-test"


By constructing the message in this way, we're doing exactly what QEWD would do with a standard WebSocket message.


If the token is for a valid QEWD Session that hasn't timed out, then the authenticate() handler function will be invoked - we'll look at that in a minute.


However, if the token is invalid, QEWD will automatically return an error to the q.handleMessage() callback function.  So that's what we check for:



      if (response.message.error) {
        res
.status(400);
        res
.send(response.message.error);
        
return;
      
}


So we'll send an error message back to the browser and stop the Middleware chain in its tracks (using that return;)


But if the token was OK, we'll allow the Middleware chain to continue, by using the next() function:



      // token was valid so Ok to upload file
      console
.log('** authorised ok!');
      
next();



So now we can perform the upload using the appropriate Multer API.


For single files we use upload.single()

For multiple files we use upload.array()


Now for a REALLY IMPORTANT bit, which can seriously catch you out.


Look at these two lines which set up the single() and array() methods:


    var uploadSingle = upload.single('singlefile');


    var uploadArray = upload.array('multifile', 5);


The first argument of each MUST be the same as the id you used in the browser's <input type=file> tag.

So go back and look at the front end code:


            <input type="file" id="singlefile" ref="file" v-on:change="handleFileUpload()"/>


            <input type="file" id="multifile" ref="files" multiple v-on:change="handleFilesUpload()"/>


Furthermore, it must be the same as the name you used in your axios functions in the front-end logic:


      formData.append('singlefile', this.file); // <=========


      for( var i = 0; i < this.files.length; i++ ){
        let file 
= this.files[i];
        formData
.append('multifile', file);  // <============
      
}


If you have a mismatch, you'll get an almost completely meaningless error that will crash the Master process - if you see such an error, check you've matched up those id values across your code!



Finally, let's take a look at the back-end application handler module:


module.exports = {
  handlers
: {
    test
: function(messageObj, session, send, finished) {
     
var incomingText = messageObj.params.text;
     
var d = new Date();
      finished
({text: 'You sent: ' + incomingText + ' at ' + d.toUTCString()});
   
},

    authenticate
: function(messageObj, session, send, finished) {
     
// if the token was invalid, QEWD will automatically have returned an error before getting here
     
// so this just needs to return a valid message if ok

     
// if the application includes a login step, you'd check here to confirm that the user
     
// has logged in - if not, return an error
     
// Check this by inspecting session.authenticated to see if it's true or false

      finished
({ok: true});
   
}
 
}
};



Note the comments about how to cater for an additional login step - typically when logging a user, your login() handler function will have set session.authenticated = true after a successful login.




So that's pretty much it - hopefully the rest of the code is self-explanatory.  All being well, when you click those file upload buttons in your browser, you'll see the files magically appear on the QEWD server in the directory path you specify, and with the same name as the original.







Arthur Ingram

unread,
Aug 8, 2018, 1:18:04 AM8/8/18
to Enterprise Web Developer Community
thank you for this 

wdbacker

unread,
Aug 8, 2018, 4:08:18 AM8/8/18
to Enterprise Web Developer Community
Rob, thank you for working out this very nice and easy example - this allows you to upload files easily using one of the most popular modern front-end frameworks these days.

FYI, Vue.js already passed React in popularity on GitHub: https://hasvuepassedreactyet.surge.sh/

Ward
Message has been deleted

Noel da Costa

unread,
Aug 18, 2018, 1:53:29 PM8/18/18
to Enterprise Web Developer Community
Sorry, I just realised I didn't post the error correctly first time:

POST http://localhost:8081/upload 404 (Not Found)


On Saturday, 18 August 2018 18:27:24 UTC+1, Noel da Costa wrote:
I'm well excited about this :)

... haven't been able to get it to work in my project though. 

The error I'm getting is: 

http://localhost:8081/upload

I wonder if this is because I'm using routes, not just config? E.g.:

var master = require('qewd').master;
var qewd = master.intercept();
master
.start(config, routes);

Rob Tweed

unread,
Aug 18, 2018, 2:05:58 PM8/18/18
to enterprise-web-de...@googlegroups.com
Noel - are you using the development web server that Vue CLI sets up for you? If so, the IP address serving up the Vue HTML pages and other resources is different from the QEWD server that provides your Rest APIs and to which you’ll be wanting to upload your files. I suspect this is the cause of your confusion. Ward may be able to provide some advice on what you need to do. What I did was compile/build my Vue source code into the www folder used by QEWD, so it was served up by the QEWD web server rather than the Vue proxy one

Btw, using the Vue proxy dev web server is why you have been running into cors issues too

Rob

Sent from my iPhone
--
You received this message because you are subscribed to the Google Groups "Enterprise Web Developer Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to enterprise-web-develope...@googlegroups.com.
To post to this group, send email to enterprise-web-de...@googlegroups.com.
Visit this group at https://groups.google.com/group/enterprise-web-developer-community.
For more options, visit https://groups.google.com/d/optout.

Noel da Costa

unread,
Aug 18, 2018, 2:19:57 PM8/18/18
to enterprise-web-de...@googlegroups.com
Hi Rob,

Yes thanks, you’re right. When I changed the address from `/upload` to `http://192.168.33.10:8080/upload` then it gives me a CORs error instead. I think maybe I can fix that with this: https://expressjs.com/en/resources/middleware/cors.html#enable-cors-for-a-single-route

They show several examples of `app.get()` but the number of arguments and format thereof are quite different to how you show it. So I'm not too sure how to integrate it into your example. 


Noel

Noel da Costa

unread,
Aug 18, 2018, 2:26:32 PM8/18/18
to Enterprise Web Developer Community
Got it working!

var cors = require('cors');

var corsOptions = {
    origin
: 'http://localhost:8081',
    optionsSuccessStatus
: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
 
};
  app
.post('/upload', cors(corsOptions), [authenticate, uploadFile]);
  app
.post('/multiple-files', cors(corsOptions), [authenticate, uploadFiles]);


Message has been deleted
Message has been deleted

Noel da Costa

unread,
Nov 5, 2018, 4:47:08 PM11/5/18
to Enterprise Web Developer Community
This method of adding middleware doesn't work for the Docker version of Qewd as far as I can tell.
How can one achieve this on the docker version?

Perhaps the docker-server/qewd.js file needs some sort of extra code that allows one to register middleware in a middleware_config.js file?

Noel da Costa

unread,
Nov 5, 2018, 4:59:17 PM11/5/18
to Enterprise Web Developer Community
I tried just adding it into the startup.js file, however this file seems to execute before installed_modules.json and thus any dependencies can't be set. 
For example I have a dependency on "cors".
So probably the installModules should happen before the startup.

Do you think you could make this change please Rob? Is it feasible?
Message has been deleted

rtweed

unread,
Nov 7, 2018, 11:48:37 AM11/7/18
to Enterprise Web Developer Community
There's 2 bits to this problem.  First is to define and export an onStarted() function inside which you should do any stuff that shouldn't begin until QEWD is properly started.

Secondly, the modules defined in your install_moduules array are, in fact, loaded before QEWD itself starts up.  However, I've found that some modules take a bit of time before they are ready for use by QEWD

What I've done in those situations is add a delay to my code within the startup.js file by using a setTimeout().  For example:

function onStarted() {

  var self = this;

  setTimeout(function() {

    // fire up your logic here

  },2000);

}


module.exports = {

  config: config,

  routes: local_routes,

  onStarted: onStarted

};





Noel da Costa

unread,
Mar 20, 2019, 10:04:28 PM3/20/19
to Enterprise Web Developer Community
I assume the logic are you suggesting should be added to the `onStarted` function is the middleware? But I don't understand how. The middleware is normally attached to the config like so:

config.addMiddleware = function(bodyParser, app, q) {...})

But the config for the Docker version is done via JSON. So how would I refactor the middleware for inclusion via `onStarted`?

Rob Tweed

unread,
Mar 21, 2019, 6:33:22 AM3/21/19
to Enterprise Web Developer Community

--
You received this message because you are subscribed to the Google Groups "Enterprise Web Developer Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to enterprise-web-develope...@googlegroups.com.
To post to this group, send email to enterprise-web-de...@googlegroups.com.
Visit this group at https://groups.google.com/group/enterprise-web-developer-community.
For more options, visit https://groups.google.com/d/optout.


--
Rob Tweed
Director, M/Gateway Developments Ltd
http://www.mgateway.com

Noel da Costa

unread,
Mar 22, 2019, 7:49:18 PM3/22/19
to Enterprise Web Developer Community
I feel like I'm getting closer. I've gotten past the CORS hurdle, and I got past the next error which was that the file was being given the wrong formData key for multer. I'm now getting another error which I don't understand:

** authorised ok!
** uploadFiles **
*** files uploaded: [
 
{
   
"fieldname": "multifile",
   
"originalname": "4k-wallpapers-24.jpg",
   
"encoding": "7bit",
   
"mimetype": "image/jpeg",
   
"destination": "upload/",
   
"filename": "df6d0e08ed85fa5bdc3030d30ff5cd81",
   
"path": "upload/df6d0e08ed85fa5bdc3030d30ff5cd81",
   
"size": 164310
 
}
]
fs
.js:137
   
throw new ERR_INVALID_CALLBACK();
   
^


TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
    at makeCallback
(fs.js:137:11)
    at
Object.rename (fs.js:571:14)
    at
/opt/qewd/mapped/startup.js:105:12
    at
Array.forEach (<anonymous>)
    at
/opt/qewd/mapped/startup.js:102:17
    at
Immediate.<anonymous> (/opt/qewd/mapped/node_modules/multer/lib/make-middleware.js:53:37)
    at runCallback
(timers.js:706:11)
    at tryOnImmediate
(timers.js:676:5)
    at processImmediate
(timers.js:658:5)
npm ERR
! code ELIFECYCLE
npm ERR
! errno 1
npm ERR
! qewd-server@2.40.1 start: `NODE_PATH=/opt/qewd/mapped/modules node qewd.js`
npm ERR
! Exit status 1
npm ERR
!
npm ERR
! Failed at the qewd-server@2.40.1 start script.
npm ERR
! This is probably not a problem with npm. There is likely additional logging output above.


npm ERR
! A complete log of this run can be found in:
npm ERR
!     /root/.npm/_logs/2019-03-22T23_44_22_018Z-debug.log

Noel da Costa

unread,
Mar 22, 2019, 8:07:56 PM3/22/19
to Enterprise Web Developer Community
This is due to a breaking change in node. `fs.rename` now requires a third parameter, which is a callback. So replace:

fs.rename(file.path, target_path)

with 

fs.rename(file.path, target_path, () => {})


Noel da Costa

unread,
Mar 26, 2019, 5:38:39 PM3/26/19
to Enterprise Web Developer Community
Given that I'm now able to upload files.. the obvious next question is how do I serve them?

Google says something like this:

app.use('/static', express.static('upload'))

... but I'm not sure where to put this?



Noel da Costa

unread,
Mar 27, 2019, 1:22:30 PM3/27/19
to Enterprise Web Developer Community
So it seems logical that this should go inside the `conf.addMiddleware` function. I've tried it and while it doesn't give any errors on start up, it also doesn't serve the static files. So my `startup.js` file looks like this

var config = require('./startup_config.json');
var local_routes = require('./local_routes.json');
var express = require('express')


config
.addMiddleware = function(bodyParser, app, q) {

  app
.use('/static', express.static('upload'))
};

module.exports = {
 config
: config,
 routes
: local_routes
};

The file path to the `upload` folder is `/opt/qewd/mapped/upload`. However browsing to this location (which does contain files) gives an error:

http://localhost:8082/static/03.jpg
Cannot GET /static/03.jpg



Noel da Costa

unread,
Mar 27, 2019, 1:31:37 PM3/27/19
to Enterprise Web Developer Community
Got it working!


 
app.use('/static', express.static('mapped/upload'))

Rob Tweed

unread,
Mar 27, 2019, 1:50:29 PM3/27/19
to Enterprise Web Developer Community
QEWD already instantiates this:

  app.use('/', express.static(config.webServerRootPath))


during its setup, so any static files that are under the web server root path can be fetched using a URL relative to the root path  


It depends on where you're wanting to save uploaded files. 


Rob



On Wed, 27 Mar 2019 at 17:31, Noel da Costa <noeld...@gmail.com> wrote:
Got it working!


 
app.use('/static', express.static('mapped/upload'))

--
You received this message because you are subscribed to the Google Groups "Enterprise Web Developer Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to enterprise-web-develope...@googlegroups.com.
To post to this group, send email to enterprise-web-de...@googlegroups.com.
Visit this group at https://groups.google.com/group/enterprise-web-developer-community.
For more options, visit https://groups.google.com/d/optout.

Michael Reach

unread,
Oct 8, 2020, 11:57:19 AM10/8/20
to Enterprise Web Developer Community
Rob, I think you may have answered this already, but...
I've been using this in qewd-up and it's been working. Only think is, the authentication step in addMiddleware.js yields a 500 error, and the Error log shows "handleMessage is not a function"

Here is the current addMiddleWare.js, with that section removed (bolded) it works fine to upload:

module.exports = function(bodyParser, app, q) {
  require('dotenv').config();

  var multer = require('multer');
  var fs = require('fs');
  var dest_path = process.env.EQAOUT_ROOT + '\\File_uploads\\IRs';
  var upload = multer({
    dest: dest_path,
    limits: {fileSize: 10000000}
    // Now send a message to vue-test worker process application handler
    //  to check validity of token
    var messageObj = {
      type: 'authenticate',
      application: 'file-upl',
      token: token
    };
  /*q.handleMessage(messageObj, function(response) {
      if (response.message.error) {
        res.status(400);
        res.send(response.message.error);
        return;
      }
    });*/

    // token was valid so Ok to upload file - DEBUGGING
    console.log('** authorised ok!');
    next();
    
  }

  function uploadFile(req, res, next) {
    // token was valid so Ok to upload file

    // security - first check extension
    if (!chkExt(req.file)) {
      res.status(400);
      res.send('File type ' + ext + ' not allowed');
      return;
    }
    
    var uploadSingle = upload.single('singlefile');
    uploadSingle(req, res, function (err) {
      if (err) {
        // something went wrong with the upload
        res.status(400);
        res.send(response.message.error);
        return;
      }
      var fldr = req.body.uploadFldr; // folder for that particular IR
      if (!fldr) fldr = "Error"
      // rename the temporary uploaded file using the original file name
      var target_path = dest_path + '/' + fldr + '/' + req.file.originalname;
      console.log(req.file.path + "," + target_path); // DEBUGGING
      fs.rename(req.file.path, target_path, function() {
        if (err) console.log(err);
      });
      res.send('Successfully uploaded!');
    });
  }

  function uploadFiles(req, res, next) {

    var uploadArray = upload.array('multifile', 5);
    uploadArray(req, res, function (err) {
      if (err) {
        // something went wrong with the upload
        res.status(400);
        res.send(response.message.error);
        return;
      }
      console.log('*** files uploaded: ' + JSON.stringify(req.files, null, 2));
      var fldr = req.body.uploadFldr; // folder for that particular IR
      if (!fldr) fldr = "Error"
      console.log('*** uploaded to folder ' + fldr);
      req.files.forEach(function(file) {
        // security - check extension
        if (!chkExt(file)) {
          console.log('File type ' + ext + ' not allowed');
          return;
        }
        
        // rename each temporary file
        var target_path = dest_path + '/' + fldr + '/' + file.originalname;
        fs.rename(file.path, target_path, function () {
          if (err) console.log(err);
        });
      });
      res.send('Successfully uploaded!');
    });
  }
  
  function chkExt(file) { // only some extensions allowed
    // allowed extensions
    var extList =
          ['txt','pdf','xls','xlsx','doc','docx','jpg','jpeg','png','tif','tiff','gif'];
        // security - first check extension
    var filenm = file.originalname;
    var ff = filenm.split("."), ext = ff[ff.length - 1];
    ext = ext.toLowerCase()
    return extList.includes(ext);
  }

  app.post('/file-upl/upload', [authenticate, uploadFile]);
  app.post('/file-upl/multiple-files', [authenticate, uploadFiles]);
};

rtweed

unread,
Oct 21, 2020, 7:32:40 AM10/21/20
to Enterprise Web Developer Community
 Michael

The way the addMiddleware module is used in QEWD-Up is a bit different to how it's used in "classic" original QEWD

Its use is, of course, documented here:


As you'll see, its arguments are a bit different from the way you've used it:

addMiddleware(bodyParser, app, router, config)

The router function is NOT what you're using a "q"

What it doesn't mention is that the "this" context within the addMiddleware function is the QEWD Master process object - what you'd assumed to be referenced as "q".

So, I think that within your example, where you're doing 

    q.handleMessage(...)  

if you change that to:

    this.handleMessage(...)

it should work!

Let me know if that does the trick

Rob


Reply all
Reply to author
Forward
0 new messages