balderdashy / sails

Realtime MVC Framework for Node.js
https://sailsjs.com
MIT License
22.84k stars 1.95k forks source link

Multiple file uploads is not working #7033

Open SiriSuri opened 4 years ago

SiriSuri commented 4 years ago

"node": "^12.16" "sails-mongo": "^1.2.0" "sails": "^1.2.3", "sails-hook-grunt": "^4.0.0", "sails-hook-orm": "^2.1.1", "sails-hook-sockets": "^2.0.0",

Above are the versions i am using. my requirements is I will be receiveing array of files from HTML along with 5 arrays of text fields.I am uploading files to my images folder and saving the file name to db using the following code.

incdesc=req.body.incdesc; idate=req.body.idate; itime=req.body.itime; sysaffected=req.body.sysaffected; solution=req.body.solution; //prevmes:preventive measures prevmes=req.body.prevmes; var a=''; console.log(incdesc,idate,itime,sysaffected,solution,prevmes) var f= req.file('ifile') console.log(req.session.uid); f.upload({dirname:'../../assets/images/logs'}, function whenDone(err, files) { if (err) return res.serverError(err); else if(files.length==0){ message="No file uploaded-log"; console.log(message); } else{ for(i=0;i<files.length;i++){ a=files[i].fd.toString();var start=a.lastIndexOf('\'); a=a.substring(start+1) console.log(a) Incident.create({username:username,recorddate:date,type:type,month:month,year:year,inc:inc,incdesc:incdesc[i+1],idate:idate[i+1],itime:itime[i+1],sysaffected:sysaffected[i+1],solution:solution[i+1],prevmes:prevmes[i+1],ifile:a}).exec(function (err,fij){ if (err) return res.serverError(err); console.log(fij)
}); }} });

This is working fine when i upload files of small size. but as the file size is increasing to 1 mb, it is able to save the file location, but the other text fields are missing. That is like only the first record is getting created with all params. from second record, only the file location is being saved and remaining fields as empty. I dont understand where i am doing wrong

sailsbot commented 4 years ago

@SiriSuri Thanks for posting! We'll take a look as soon as possible.

In the mean time, there are a few ways you can help speed things along:

Please remember: never post in a public forum if you believe you've found a genuine security vulnerability. Instead, disclose it responsibly.

For help with questions about Sails, click here.

crh3675 commented 4 years ago

Not sure how relevant with version 1.0 but we had similar issue and needed to adjust the config/http.js by overriding the defaults.

      multipartParser: require('multer')({
         dest: './uploads',
         limits: {
            fieldSize: 1000 * 1000 * 5,
            fileSize: 1000 * 1000 * 50
         }
      }).any(),

      urlencodedParser: bodyParser.urlencoded({
         limit: '50mb',
         extended: true
      }),

      jsonParser: bodyParser.json({limit: '50mb'}),

      rawParser: bodyParser.raw({limit: '50mb'}),

      textParser: bodyParser.text({limit: '50mb'})

And top of file:

module.exports.http = {
   /****************************************************************************
    *                                                                           *
    * Express middleware to use for every Sails request. To add custom          *
    * middleware to the mix, add a function to the middleware config object and *
    * add its key to the "order" array. The $custom key is reserved for         *
    * backwards-compatibility with Sails v0.9.x apps that use the               *
    * `customMiddleware` config option.                                         *
    *                                                                           *
    ****************************************************************************/

   middleware: {

      /***************************************************************************
       *                                                                          *
       * The order in which middleware should be run for HTTP request. (the Sails *
       * router is invoked by the "router" middleware below.)                     *
       *                                                                          *
       ***************************************************************************/

      order: [
         'startRequestTimer',
         'cookieParser',
         'session',
         'myRequestLogger',
         'urlencodedParser',
         'jsonParser',
         'rawParser',
         'textParser',
         'multipartParser',
SiriSuri commented 4 years ago

Hi this is what i made in my http.js based on your suggestion. /**

module.exports.http = {

/****

};

But it is showing errors. error is : ReferenceError: bodyParser is not defined can you help me to resolve this?

crh3675 commented 4 years ago

You need to install multer (for multipart) in order to override. I provided a full config that worked for me.

npm install multer --save

Here is a full file for you:

/**
 * HTTP Server Settings
 * (sails.config.http)
 *
 * Configuration for the underlying HTTP server in Sails.
 * (for additional recommended settings, see `config/env/production.js`)
 *
 * For more information on configuration, check out:
 * https://sailsjs.com/config/http
 */
const multer = require('multer');
const bodyParser = require('body-parser');

// Set max upload sizes
const uploadsPath = './uploads';
const maxUploadSize = 50; // mb
const maxUploadFields = 1000;
const maxUploadOptions = {
  limit: `${maxUploadSize}mb`
};

// Explicit parsers to control size of
// uploaded data
const parsers = {
  // Review documentation on multer for more advanced configuration
  // http://expressjs.com/en/resources/middleware/multer.html
  multipart: multer({
    dest: uploadsPath,
    limits: {
      fieldSize: maxUploadFields,
      fileSize: 1000 * 1000 * maxUploadSize
    }
  }).any(),

  urlencoded: bodyParser.urlencoded({
    limit: maxUploadOptions.limit,
    extended: true
  }),

  json: bodyParser.json(maxUploadOptions),
  text: bodyParser.text(maxUploadOptions),
  raw: bodyParser.raw(maxUploadOptions)
};

module.exports.http = {

  /****************************************************************************
  *                                                                           *
  * Sails/Express middleware to run for every HTTP request.                   *
  * (Only applies to HTTP requests -- not virtual WebSocket requests.)        *
  *                                                                           *
  * https://sailsjs.com/documentation/concepts/middleware                     *
  *                                                                           *
  ****************************************************************************/

  middleware: {

    /***************************************************************************
    *                                                                          *
    * The order in which middleware should be run for HTTP requests.           *
    * (This Sails app's routes are handled by the "router" middleware below.)  *
    *                                                                          *
    ***************************************************************************/

    // order: [
    //   'cookieParser',
    //   'session',
    //   'bodyParser',
    //   'compress',
    //   'poweredBy',
    //   'router',
    //   'www',
    //   'favicon',
    // ],

    order: [
      'startRequestTimer',
      'cookieParser',
      'session',
      // Explicit body parser updates
      'urlencodedParser',
      'jsonParser',
      'rawParser',
      'textParser',
      'multipartParser',
      // body parser done
      'compress',
      'router',
      'www',
      'favicon'
    ],

    urlencodedParser: parsers.urlencoded,
    multipartParser: parsers.multipart,
    jsonParser: parsers.json,
    textParser: parsers.text,
    rawParser: parsers.raw
  },
};
SiriSuri commented 4 years ago

Can you please suggest any tutorial through which i can learn file uploading thorigh multer. I tried ur code. No errors in http.js. but there is an error in my code. my code is: vapt1file=req.file("vapt1file"); vapt1file.upload({dirname:'../../assets/images/logs'}, function onUpload(err, files) { if (err) { message="error occured.please try again"; console.log(err); FlashService.error(req,message); res.redirect('/dashboard/home') // res.view('',{rid:req.params.id}) }
else if(files.length==0){ // var a= await Vapt.find({vno:id}); // console.log(a[0].loglocation); message="No file uploaded-log"; console.log(message); logid= Vapt.update({id:id}).set({vapt1file:'',vapt1:false,type:type,recorddate:date}).exec(function(err){ if(err) console.log(err) else console.log('success'); }) } else{ loglocation=files[0].fd.toString();var start=loglocation.lastIndexOf('\'); loglocation=loglocation.substring(start+1) console.log(loglocation) Vapt.update({id:id}).set({vapt1file:loglocation,vapt1:true}).exec(function(err,user){ if(err) console.log(err) else console.log(user) }) }});

this is the error. req.file is not a function.

May be I should modify my code as per multer? Can you suggest that Please?

crh3675 commented 4 years ago

Try req.files instead.

.any() Accepts all files that comes over the wire. An array of files will be stored in req.files.

That would be an array of objects that you can iterate through:

{
  "fieldName": 'vapt1file',
  "originalName": "orig name"
  "path": 'the uploaded path';
  "size": 'the size',
  "mimeType": 'some type' 
}

You can access using a filter:

const vapt1file = req.files.find(f => f.fieldname === 'vapt1file');

Not sure of your entire files contents but I cleaned up in order to help. One thing, when posting code examples, please apply code-formatting as it is much easier to read.


const vapt1file = req.files.find(f => f.fieldname === 'vapt1file');
const updates = {
  vapt1file: '',
  vapt1: false,
  type: type, // IS THIS DEFINED SOMEWHERE ELSE?
  recorddate: date // IS THIS DEFINED SOMEWHERE ELSE?
};

if (vapt1file) {
  updates.vapt1file = vapt1file.path;
  uppdates.vapt1 = true;
} else {
  sails.log.info('No file uploaded')
}

Vapt.update({id:id}).set(updates).exec(function(err){
  if(err) {
    sails.log.error(err);
    FlashService.error(req, err.toString());
    res.redirect('/dashboard/home');
  } else {
    sails.log.info('success');
    res.redirect('/dashboard/home');
  }
});
SiriSuri commented 4 years ago

Hi.. Thanks for the suggestion. I am facing errors with req.files also. the error is: req.files is not a function I just didnt understand how to use multer in sails js. This is what I tried. Being new to JS, I cant understand anything clearly.

const upload=require("multer");
incdesc=req.body.incdesc;
idate=req.body.idate;
itime=req.body.itime;
sysaffected=req.body.sysaffected;
solution=req.body.solution;
prevmes=req.body.prevmes;
 var f= req.files('ifile')
            f.upload(function whenDone(err, files) {
                      if (err)  return res.serverError(err);
                      else if(files.length==0){
                         message="No file uploaded-log";
                         console.log(message);
                       }
                       else{
                       for(i=0;i<files.length;i++){
                       a=files[i].fd.toString();var start=a.lastIndexOf('\\');
                       a=a.substring(start+1)

                Incident.create({incdesc:incdesc[i+1],idate:idate[i+1],itime:itime[i+1],sysaffected:sysaffected[i+1],solution:solution[i+1],prevmes:prevmes[i+1],ifile:a}).exec(function (err,fij){
                        if (err) return res.serverError(err); 
                        console.log(fij)   
             });   }}
            });

in your code, you didnot use any upload function. How will it work?

crh3675 commented 4 years ago

Does your HTML

have the correct enctype?

<form method="post" enctype="multipart/form-data">
crh3675 commented 4 years ago

Are you running this process in a controller file? Can you post the entire file?

SiriSuri commented 4 years ago

yeah I am running it in a control file. I am having multipart/form-data.

const upload=require("multer");
module.exports = {
saveincident: function(req,res){
if(req.session.authenticated){
  var incdesc=[];var idate=[];var itime=[];var sysaffected=[];var solution=[]; var prevmes=[];
  var message;
var year=new Date().getFullYear();
            var username=req.session.username;
            var month=new Date().getMonth()-1;
            if(month<0) month=11;
            var date = new Date().toISOString().slice(0,10);
            var type=req.body.type;
            var inc=req.body.inc;
            if(inc=='true') inc=true;
            if(inc=='false') inc=false;
            if(inc){
              console.log("in if")
              incdesc=req.body.incdesc;
              idate=req.body.idate;
                     itime=req.body.itime;
                     sysaffected=req.body.sysaffected;
                     solution=req.body.solution;
                    //prevmes:preventive measures
                     prevmes=req.body.prevmes;
                          var a='';
                     console.log(incdesc,idate,itime,sysaffected,solution,prevmes)
                    var f= req.files('ifile')
            f.upload(function whenDone(err, files) {
                      if (err)  return res.serverError(err);
                      else if(files.length==0){
                         message="No file uploaded-log";
                         console.log(message);
                       }
                       else{
                       for(i=0;i<files.length;i++){
                       a=files[i].fd.toString();var start=a.lastIndexOf('\\');
                       a=a.substring(start+1)
                       console.log(a)
                       Incident.create({username:username,recorddate:date,type:type,month:month,year:year,inc:inc,incdesc:incdesc[i+1],idate:idate[i+1],itime:itime[i+1],sysaffected:sysaffected[i+1],solution:solution[i+1],prevmes:prevmes[i+1],ifile:a}).exec(function (err,fij){
                        if (err) return res.serverError(err); 
                        console.log(fij)   
             });   }}
            });
          }
         else{
          console.log("in else")
           Incident.create({inc:inc,username:username,recorddate:date,type:type,month:month,year:year}).exec(function (err,fij){
            if (err) return res.serverError(err); 
            console.log(fij)   
 }); 
         }
         res.redirect('/dashboard/home');
        }
          else{
            message="Please Login to View the page";
            FlashService.error(req,message);
           res.redirect('/')  }
            },
},

This is my controller function. How to incorporate multer in this?

crh3675 commented 4 years ago

req.files is not a function, it is an Array. You need to get the file object from out of the array. Meaning the lines where you get the file info should be:

var f = req.files.find(f => f.fieldname === 'ifile');

Which will return the object. You also do not need this line const upload=require("multer"); and you don't need to use the f.upload code as the file will already be in an uploaded directory.

SiriSuri commented 4 years ago

ok so in controller I dont need to do anything about uploading.. Do i need to do anything in my html i.e. ejs in my case?

crh3675 commented 4 years ago

Upload errors will automatically be sent back to the ExpressJS routing framework so you do not need to handle upload errors. As long as you have correct enctype and file input with name <input type="file" name="ifile"/> then you should have access to that uploaded file in req.files Array.

SiriSuri commented 4 years ago

Files are getting stored in my directory automatically. But there is no extension for the files. I was unable to open those files. I wan t to display the pdf files in my browser after saving it in my server. But no extesion is being saved. How can I do that

crh3675 commented 4 years ago

I believe that is a security feature. You will need to rename manually before saving to your database. So replace that previous upload functionality with a rename instead.

const path = require('path');
const fs = require('fs');
const ext = path.extname(f.originalname);
const savePath = f.path + ext;
fs.rename(f.path, savePath, (err) {
   if (err) sails.log.error(err);
});

File names are randomized to prevent overwrite on the server.

SiriSuri commented 4 years ago

Thank you so much.. Its working fine..

crh3675 commented 4 years ago

Glad to have helped!