I don't understand THIS function structure in JS - javascript

im having problems to understand this function structure, I would thank a lot a simple explanation. To put into context Im creating a storageEngine in a const called storage, that is what multer library uses to save files from a web application. But what I don`t understand is why the function diskStorage receives like an object with two atributes (destination and filename), which in turn are asigned a function() that calls a cb(req, file, cb) that finally gives the value that is required. I mean I know what destination and filename will be used for in multer, but what i dont understand is how it is created here, the JS structure.
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public/assets");
},
filename: function (req, file, cb) {
cb(null, file.originalname);
},
});
const upload = multer({ storage });

Related

Uploading a file using multer in Node JS

Can you please explain in detail what is happening in these lines of code?
Why we are storing a function in the object with a callback?
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
The multer library is going to call the destination function when it wants to know what directory you want to put the uploads into. It will call the filename function when it wants to now what filename you want to give a particular upload.
The reason both of these are functions is so that you have the option of examining the incoming request to compute a directory or filename that is appropriate for that specific request. For example, if you wanted to store all files uploaded by one particular user in a directory for that specific user, you might look at the req.session object to determine which user it is making the upload and then you would compute the appropriate directory for that user.
The destination option may be provided as a plain string instead of a function if the value is known and is constant for all requests. For example, you could do this:
var storage = multer.diskStorage({
destination: '/tmp/my-uploads',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
The reason that you can communicate the final result via a callback rather than just a plain return value is in case you need to do some asynchronous operation (like look something up in a database) in order to compute what the directory or filename should be. Communicating the result back by calling their supplied cb(...) function allows you do asynchronous operations and communicate back the result asynchronously.

How to avoid a form being resubmitted in node.js / express / multer when there's no response?

I have a problem in that sometimes my form (submitted in a standard way via the Node.Js app) takes too long to be processed.
On the backend (app.js) the logic looks like this:
var multer = require('multer')
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './temp')
},
filename: function(req, file, cb) {
cb(null, file.originalname)
},
})
var upload = multer({
storage: storage
})
app.post(
'/import',
upload.array('uploadedFiles', 50),
imports.submit
)
So what happens then is that the POST request happens again (probably after not receiving any response) and the files get resubmitted. My log shows /POST --/--.
How can I avoid having that happen?
I tried to use the following code:
$('#submitform').on('submit', function(e){
e.preventDefault();
$(this).find('input[type=submit]').attr('disabled', 'disabled');
$('#message').html('Importing the data');
$("#submitform").unbind('submit').submit()
});
But it simply does not submit the form.
How could I fix this issue?
I can't use AJAX as the response I get from the node.js app as I'd have to rewrite too much backend.

req.files does not exist while sending form data in nodejs

I am tying to store a form data along with files. when I am use form without this enctype="multipart/form-data" works fine but req.files dosent exist in req which i need to upload files. and when i use enctype in form, still req.files dosent exist and req.body does not have any data.
I was trying to implement multer to handle files but req.files dosent exist so didnt get any idea.
my route
const urlencodedParser = bodyParser.urlencoded({extended: true});
router.post('/save_file', urlencodedParser, home.add_file);
my controller
exports.add_file = function(req, res){
console.log(req.body);
console.log(req.files);
}
Any help will be appriciated.
bodyParser, the library you use to parse your request from the server, doesn't parse files, you need to use another library, (multer is very good and easy).
So first :
Install Multer: npm install multer --save
Here the link of Multer: https://github.com/expressjs/multer
Use this example as base:
let multer = require("multer"); //the library
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'path/to/upload/your/file');
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});//Configure the place you will upload your file
let upload = multer({ storage: storage }); //instanciation of multer
// image is the name of the input in the form
router.post('/your_endpoint', upload("image"), (req, res)=> {
let file = req.file; //contains the file
let path = file.path; //contains the paths
})
Try using multer in your node app -
https://www.npmjs.com/package/multerenter link description here

Dynamically create folder and upload file in multipart request Node js

I'm trying to dynamically create folders when user loads a file (this is because I need to store every user files separately so there's no conflict if a file is deleted) and create the file path in my computer so I can store it in my many to many relationship table in the database (users can have many files and many files can belong to different users, that's why I can't store them together) but I cannot get this with my current logic. I could create different name with timestamps but it would get messy.
I'm using multer and Express. The form data that's been sent is the one on the attached image.
const express = require('express');
const app = express();
const multer = require('multer');
const bodyParser = require('body-parser');
const fs = require('fs');
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: false }));
let storage = multer.diskStorage({
destination: function (req, file, cb) {
//req.body is empty
let path = `./public/uploads/${req.body.id}`;
if (!fs.existsSync(path)) {
fs.mkdirSync(filesDir);
}
cb(null, path);
},
filename: function (req, file, cb) {
cb(null, `${file.originalname}`)
}
})
let upload = multer({storage:storage});
var cpUpload = upload.fields([{ name: 'file', maxCount: 1}, { name: 'id'}]);
router.post('/uploadSingFile', cpUpload,(req,res)=>{
console.log(req.body); req.body is ok
res.send({status:200});
});
Somehow the request object doesn't have the body in the destination function but does in the post request. I thought this would work given the way middlewares work in Node, the storage function should have the same request object that the post request has.
You can use newly generated uuid for filenames to have it unique inside the folder. For the folder name, instead of getting it from request body(req.body.id), use something like userId which can make sure the folder belongs to particular user.

NodeJS Multer: configuration not applied

I have a simple upload application written in NodeJS using Multer, which works fine. Here's the code:
var express = require('express'),
bodyParser = require('body-parser'),
qs = require('querystring'),
multer = require('multer'),
logger = require('morgan');
var config = require('./config'),
oauth = require('./oauth');
function extractExtension(filename) {
return filename.split('.').pop();
}
function getRandom(min, max) {
return Math.floor(Math.random() * max) + min;
}
var app = express();
//app.use(cors());
// Add headers
app.use(function(req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Authorization,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
// Multer
var momentUpload = multer({
dest: './uploads/',
limits: {
fileSize: 256 * 1024 * 1024
},
rename: function(fieldname, filename) {
return Date.now() + '-' + getRandom(100000, 999999) + '.' + extractExtension(filename);
},
onFileUploadStart: function(file) {
console.log(file.originalname + ' is starting ...')
},
onFileUploadComplete: function(file) {
console.log(file.fieldname + ' uploaded to ' + file.path)
}
}).single('file');
app.set('port', 4000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.post('/file/upload', [oauth.ensureAuthenticated, momentUpload, function(req, res) {
console.log(req.body); // form fields
console.log(req.file); // form files
res.status(204).end();
}]);
// Start the Server
app.listen(app.get('port'), function() {
console.log('Metadata store env: ' + config.METADATASTORE_ENV);
console.log('Express server listening on port ' + app.get('port'));
firebase.authenticate();
console.log('Connected to Firebase');
});
The problem is, however, that the configuration of Multer doesn't seem to be working at all. The destPath works, and files appear in the folder I provided (./uploads/). Larger file sizes are allowed (e.g. a file of 400MB, while the options clearly state 256MB), and the callback functions aren't fired once. There is no error message. Any idea what I'm doing wrong here? I followed guides on Google and on the official page, but can't get it to work.
First of all, multer has changed its API recently, so it no longer accepts rename, onFileUploadStart or onFileUploadComplete!
We can take a look at the API here https://github.com/expressjs/multer, so lets analyse the new way things work!
NOTE: If you haven't updated your multer version, I strongly advise you to, because older versions are suspected to have a security breach.
Basic Usage
Multer accepts an options object, the most basic of which is the dest
property, which tells Multer where to upload the files. In case you
omit the options object, the files will be kept in memory and never
written to disk.
By default, Multer will rename the files so as to avoid naming
conflicts. The renaming function can be customized according to your
needs.
The options object also accepts fileFilter(a function to control which files are uploaded) and limits(an object that specifies the size limits) parameters.
So, your code would look like this (concerning only the multer part and considering you'd want to use all options which you don't have to):
// Multer
var momentUpload = multer({
dest: './uploads/',
limits: {
fileSize: 256 * 1024 * 1024
},
fileFilter: function(req, file, cb) {
// The function should call `cb` with a boolean
// to indicate if the file should be accepted
// To reject this file pass `false`, like so:
cb(null, false)
// To accept the file pass `true`, like so:
cb(null, true)
// You can always pass an error if something goes wrong:
cb(new Error('I don\'t have a clue!'))
}
}).single('file');
If you want to have more control of the storing of files you can use a storage engine. You can create your own or simply use the ones available.
The available ones are: diskStorage to store files on disk or memoryStorage to store files in the memory as Buffer objects.
Since you clearly want to store files in the disk, let's talk about the diskStorage.
There are two options available: destination and filename.
destination is used to determine within which folder the uploaded
files should be stored. This can also be given as a string (e.g.
'/tmp/uploads'). If no destination is given, the operating system's
default directory for temporary files is used.
Note: You are responsible for creating the directory when providing
destination as a function. When passing a string, multer will make
sure that the directory is created for you.
filename is used to determine what the file should be named inside the folder. If no filename is given, each file will be given a random name that doesn't include any file extension.
So, your code (concerning only the multer part) would look like this:
// Multer
//Storage configuration
var storageOpts = multer.diskStorage({
destination: function (req, file, cb) {
//Choose a destination
var dest = './uploads/';
//If you want to ensure that the directory exists and
//if it doesn't, it is created you can use the fs module
//If you use the following line don't forget to require the fs module!!!
fs.ensureDirSync(dest);
cb(null, dest);
},
filename: function (req, file, cb) {
//here you can use what you want to specify the file name
//(fieldname, originalname, encoding, mimetype, size, destination, filename, etc)
cb(null, file.originalname);
}
});
var momentUpload = multer({
storage: storageOpts,
limits: {
fileSize: 256 * 1024 * 1024
},
fileFilter: function(req, file, cb) {
// The function should call `cb` with a boolean
// to indicate if the file should be accepted
// To reject this file pass `false`, like so:
cb(null, false)
// To accept the file pass `true`, like so:
cb(null, true)
// You can always pass an error if something goes wrong:
cb(new Error('I don\'t have a clue!'))
}
}).single('file');
Hope it helped! :)

Categories

Resources