Node JS Multer Multiple Files Req.File Undefined - javascript

FIXED: ( NO req.file ) ( YES req.files )
My project need multiple files upload,
If i single image upload working, multiple image upload working ( upload to files ) and i need req.file.filename because i write mysql this image road.
Req.File = Undefined..
// Sinle image upload and write mysql..
Router.post('/uax_items_addOne', Upload.single('fileUrl'), Controller.Cnt_AddOne);
// Multiple image upload and undefined req.file
Router.post('/uax_items_multipleFile', Upload.array('fileUrl', 12), Controller.Cnt_MultipleFile);
// This is controller
exports.Cnt_MultipleFile = (req, res, next) => {
console.log(req.file); // This is write "undefined"
}
// This is my Storage
const Storage = Multer.diskStorage({
destination: function(req, file, callback) {
callback(null, './uax_Uploads/images');
},
filename: function(req, file, callback) {
callback(null, 'img-' + Date.now() + Path.extname(file.originalname));
}
});
var Upload = Multer({
storage: Storage
});

Multiple files are provided to you in req.files (note the s at the end).
If you want to simplify your POST queries, I would recommend using Multer's .fields() function, that let you group incoming file params (post-processing is done, both, with the files field).
For example:
upload.fields([
{ name: 'one_tag_param' , maxCount: 1 },
{ name: 'multiple_tag_param' , maxCount: 12 }
])

Related

How to save image coming from multer memory disk?

I use multer in nodejs to handle multipart/formdata request and get the image file on the request like this :
import multer from "multer";
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 1000000000, files: 2 },
});
app.post("/", upload.single("image"), (req, res , next) => {
const imageFile = req.file
dbx
.filesUpload({ path: "/image.png", contents: imageFile })
.then((response: any) => {
})
.catch((uploadErr) => {
});
}
)
The problem is I can't upload the image and it gives me error that it's a Buffer not an actual image . How can I generate the image from req.file then upload it without saving it on the disk ?
You can decode your data or change it's encoding but to convert it to an image doesn't make any sense.
The example code provided by dropbox here, reads a file inutf-8 and uploads it.
You can also do that same by taking the buffer and uploading it.
const imageFile = req.file.buffer;
Solved the issue by sending the file buffer itself to dropbox like this
const imageFile = req.file && req.file.buffer;
dbx
.filesUpload({ path: "/life.png", contents: imageFile })
.then((response: any) => {
console.log("SUccess");
})
.catch((uploadErr) => {
console.log("ERRRR");
console.log(uploadErr);
});

how recovery of multiple downloaded image in formData with express?

Hello sorry for my english, i have a little problem. i try to upload many images but in back side i have just one image, (i use React express formidable cloudinary) here is my code front :
const [arrayFiles, setArrayFiles] = useState([]);
const handleFiles = (e) => {
let arrayUpload = [...arrayFiles];
arrayUpload.push(e.target.files[0]);
setArrayFiles(arrayUpload);
};
const handleSubmit = async (e) => {
arrayFiles.forEach((file) => {
formData.append("image", file);
});
const response = await axios.post(
"http://localhost:3100/offer/publish",
formData
);
here is my code back but req.files => just one image
my page route :
router.post("/offer/publish", async (req, res) => {
console.log(req.files);
const result = await cloudinary.uploader.upload(req.files.image.path, {
folder: `api/leboncoin/offers/${newOffer._id}`, // _id vient de la création du newOffer au dessus
public_id: "preview",
cloud_name: process.env.CLOUDINARY_NAME,
});
my page index.js:
page index.js :
const express = require("express");
const formidable = require("express-formidable");
const mongoose = require("mongoose");
const cloudinary = require("cloudinary").v2;
const cors = require("cors");
const app = express();
app.use(cors());
app.use(formidable({ multiples: true }));
You only get one file in req.file as you've set your multer.single
Using multer
There are 3 ways you can handle multiple file upload, each with a slightly different taste.
Assume you have a base multer
const storage = multer.diskStorage({
destination: "public/data/",
filename: function(req, file, cb){
// You may change this to however you want, only affect your file name
crypto.randomBytes(20, (err, buf) => {
cb(null, buf.toString("hex") + path.extname(file.originalname))
})
}
});
const upload = multer({ storage: storage });
Use .any()
Accepts all files that comes over the wire. An array of files will be stored in req.files.
WARNING: Make sure that you always handle the files that a user uploads. Never add multer as a global middleware since a malicious user could upload files to a route that you didn't anticipate. Only use this function on routes where you are handling the uploaded files.
router.post("/offer/publish",upload.any(), async (req, res) => {
console.log(req.files); // Should give you an array of files
// Do anything else
});
Use .array(fieldname[, maxCount])
Accept an array of files, all with the name fieldname. Optionally error out if more than maxCount files are uploaded. The array of files will be stored in req.files.
router.post("/offer/publish",upload.array('someFieldName', 10), async (req, res) => {
console.log(req.files); // Should give you an array of files
// Do anything else
});
Use .fields(fields)
Accept a mix of files, specified by fields. An object with arrays of files will be stored in req.files.
fields should be an array of objects with name and optionally a maxCount. Example:
router.post(
"/offer/publish",
upload.fields([
{
name: "image",
maxCount: 1,
},
{
name: "audio",
maxCount: 1,
},
]),
async (req, res) => {
console.log(req.files.image[0]);
console.log(req.files.audio[0]);
// Do anything else
}
);
For your case, I would recommend going with Option 2.

can't change name to uploaded file using multer

I am trying to change the name of the image im uploading to the server using multer which gives the file a random name.
I user multer.diskStorage method to do so as described in the documentation but it keeps saving the file with random names
CODE:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null,'./uploads/')
},
fileName: (req,file,cb) => {
cb(null, file.originalname)
}
})
const upload = multer({storage: storage})
router.post('/', upload.single('carImage') ,(req, res) => {
res.send(req.file);
}
RESPONSE :
{
fieldname: 'carImage',
originalname: 'testOCR8.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: './uploads/',
filename: '229f70c20e5550dbe638db49791ef17d',
path: 'uploads/229f70c20e5550dbe638db49791ef17d',
size: 1712380
}
im uploading to the server using multer which gives the file a random name
You made a typo. It is filename not fileName. This is the standard behavior as per the docs.
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 should be
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null,'./uploads/')
},
filename: (req,file,cb) => { // notice the change 'filename'
cb(null, file.originalname)
}
});
const upload = multer({storage: storage});
Try a different approach for using StorageMulter. Try the following -
var StorageMulter = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, "./temp");
},
filename: function(req, file, callback) {
var uploadFileName = "x.jpg"; //Manipulate this variable accordingly
callback(null, uploadFileName);
}
});
var upload = multer({
storage: StorageMulter
});
app.post("/api/uploaddocument", function(req, res) {
upload(req, res, function(err) {
if (err) {
return res.end("Something went wrong!"+ err);
}
});
});

Full control over files sent with ajax in nodejs

I use multer to parse multiple files sent as multipart/data-form with axios
...
const storage = multer.diskStorage({
destination: './gallery',
filename(req, file, cb) {
(1) ....
},
});
const upload = multer({ storage });
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
(2) ...
})
.catch(..)
})
...
at this point everything is fine and my images are saved.
the problem is that i want to make a loop in (1) or (2) and name my files like this
files.forEach((file, index) => {
// rename file to => product_id + '_' + index + '.jpeg'
}
For example if i have 3 files they will be named to
5a9e881c3ebb4e1bd8911126_1.jpeg
5a9e881c3ebb4e1bd8911126_2.jpeg
5a9e881c3ebb4e1bd8911126_3.jpeg
where 5a9e881c3ebb4e1bd8911126 is the id of the product document saved by mongoose.
how to solve this naming issue ?
is multer the best solution cause i want full control over my files ?
Is there a better approach with another node package ?
is it good to send images as multipart/data-form or data URL base64 ?
This is easy, as long as you understand how express works. So before jumping to solution its important to have a clear understanding.
When you have a express code like below
router.post('/abc', function(req, res) {res.send('hello world');})
Express passes the request from chains of middlewares/functions. Now each function gets req, res, next parameters. The next is function, which a middleware is suppose to call when the processing is complete. If the middleware decides not to call next the request ends there and no more middlewares are called further.
When we used function(req, res) {res.send('hello world');}, we didn't take the next parameter at all, which means we are not interested in any other code to do anything. Now getting back to our problem
router.post('/products', upload.array('images'), (req, res, next) => {...}
You have used upload.array('images') first and then your actual product creation code. So I would show two approaches to solve this problem
One more middleware to rename the files
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
req.product = product
next();
})
.catch(..)
}, (req, res, next) => {
//Get the product id using req.product
//Move the files as per the name you desire
})
Reverse the processing order
In this approach you first create the product and then let image processing happen. I have created a sample for the showing the same
let express = require('express');
let bodyParser = require('body-parser');
app = express();
let multer = require('multer');
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
console.log('Product id - ' + req.product_id);
cb(null, req.product_id + '.js');
},
});
const upload = multer({ storage });
app.all('/', (req, res, next) => {
console.log('Hello you');
promise = new Promise((resolve) => {
// simulate a async product creation
setTimeout(() => resolve(1234), 100);
});
promise.then((product_id) => {
console.log('create the product and get the new product id')
// set the product id in the request object, so the multer
// filename function can access it
req.product_id = product_id;
res.send('uploaded files');
if (next)
next();
});
}, upload.array('images'));
module.exports = {
app
};
app.listen(8020);
And testing it using postman works fine
Edit: 19-Mar-2018
For multiple files you can easily update your filename function code like below
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
req.file_id = req.file_id || 0;
req.file_id++;
console.log('Product id - ' + req.product_id);
cb(null, req.product_id +'_' + req.file_id +'.js');
},
});
This will make sure that you get all the files for that product. Now coming to your questions
how to solve this naming issue ?
This answer already does that
is multer the best solution cause i want full control over my files ?
I can't say, as long it works and does what you want, it should be good enough
Is there a better approach with another node package ?
I couldn't find lot of packages. But you can explore this if you want
is it good to send images as multipart/data-form or data URL base64 ?
I would use multipart/data-form, so that no base64 conversion is needed at client side. But again this is a matter of opinion as well.
You can't set the name purely in (1) since at that point you do not know the ID of the product yet.
You can't set the name purely in (2) since at that point the files have already been saved (with filename generated by your filename(req, file, cb) function).
So I think the best solution might be to move the files after they are uploaded.
This could be done in (2). When you process the files in the router, req.files will be an array of files that have already been uploaded.
In your promise callback for Product.create, you have access to the product (which you need for the ID) and the list of files (which you need for the number).
For that, you could use fs.rename(oldPath, newPath, callback).
https://nodejs.org/docs/latest/api/fs.html#fs_fs_rename_oldpath_newpath_callback
Something like this should work:
Product.create(...).then((product) => {
req.files.forEach((file, index) => {
// file.path is the full path to the file that was uploaded.
// newPath is where you want to put it.
// Let's use the same destination and just change the filename.
const newPath = file.destination + product.id + '_' + index
fs.rename(file.path, newPath)
})
})

Multer : Setting different destinations in the destination Property

I have a case where I need to store images in different directories . So I have set the multer as.
app.use(multer({
dest: path.join(__dirname, '`public/assets/img/profile`'),
rename: function (fieldname, filename, req, res) {
if(req.session.user) return req.session.user.id;
else if(req.session.doctor) return req.session.doctor.id;
}
}));
However I need more destinations to store images.
public/assets/img/picture1
I have seen similar questions but I could not understand any of them.
Any help would be greatfull.
Your example is from quite old version of multer. I strongly recommend you to use latest version (due to security reasons).
If you are sure that you need old version then just add to your multer options:
app.use(multer({
//...
changeDest: function(dest, req, res) {
return dest + '/user1';
}
//...
}));
More details you will get in documentation (link to old version) link
Newest version of multer works a little different. This would be to big offtopic to write in detail how to use new version of multer. You easily will find in stackoverflow answer or from actual version of documentation link
I only write how to change destination directory (example from docs):
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 })
app.post('/profile', upload.single('picture'), function (req, res, next) {
// req.file is the `picture` file
// req.body will hold the text fields, if there were any
})

Categories

Resources