I'm hung up on this issue using the multer library on my Node server. I'm trying to upload a file or files and then make a call to the database for each file and then send back success or failure to the client.
So my Node server route for uploading the file(s) looks like this:
app.post('/uploadFiles', (req, res) => {
upload(req, res, async function (err) {
...
Then that upload function is simply this:
const upload = multer({ storage: multerStorage }).array('file');
And then the multerStorage function that handles the destination and filename for multer:
const multerStorage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.env.LABELS_LOCATION);
},
filename: async function (req, file, cb) {
// save file info to DB
const itemID = file.originalname.slice(0, -4);
const addLabelResult = addLabelToDB(itemID, 0);
// console.log({ addLabelResult });
// console.log({ itemID });
// console.log({ file });
cb(null, file.originalname);
},
});
And finally the addLabelToDB function:
const addLabelToDB = (itemID, enterType) => {
const sp = 'insertLabelHistory';
const inputs = [
{ name: 'itemID', type: sql.NVarChar, value: itemID },
{ name: 'enterType', type: sql.Bit, value: enterType },
];
return executeSP(res, sp, inputs);
};
So the issue is that, if you look at the addLabelToDB function, you can see the call to executeSP takes in a res. And I want that res from the original /uploadFiles route. But I don't know how to access it from here. And if there a better completely different way to do this, I am all ears.
Maybe you already solved your issue but for future people with the same issue here it goes:
Remember that you can access res from req by req.res
In your case you can just pass res to addLabelToDB, here's how:
const multerStorage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.env.LABELS_LOCATION);
},
filename: async function (req, file, cb) {
// save file info to DB
const itemID = file.originalname.slice(0, -4);
const addLabelResult = addLabelToDB(req.res, itemID, 0);
cb(null, file.originalname);
},
});
And then on your addLabelToDB:
const addLabelToDB = (res, itemID, enterType) => {
const sp = 'insertLabelHistory';
const inputs = [
{ name: 'itemID', type: sql.NVarChar, value: itemID },
{ name: 'enterType', type: sql.Bit, value: enterType },
];
return executeSP(res, sp, inputs);
};
Related
I need to get the filename info from two files sended with multer, but when I try to send a JSON with the response, it sends a Object: null prototype and the info from the images. But I canĀ“t access to it. What can I do?
Here is my code:
// Multer File
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, path.join(__dirname, "../../imagesDirectory"));
},
filename: (req, file, cb) => {
cb(
null,
`${req.params.campana}-${req.params.metodo}-${file.fieldname}-${new Date()
.toISOString()
.replace(/:/g, "-")}-${file.originalname}`
);
},
});
const upload = multer({ storage: storage });
const uploads = upload.fields([
{ name: "image1" },
{ name: "image2" },
{ name: "image3" },
{ name: "image4" },
{ name: "image5" },
{ name: "image6" },
{ name: "image7" },
]);
module.exports = uploads;
// Controller
const uploadFile = async (req, res) => {
if (!req.files) {
console.log("You need to add an image");
res.json({ err: "You need to add an image" });
} else {
console.log(req.files);
let name = req.files.filename;
res.json({ msg: "Image Upload", rows: name });
}
};
module.exports = { uploadFile };
i'm trying to upload two files with different file extensions with multer from two fields but when i try it with postman the result always for the file is null, what is the solution for my problem? here is my code
middleware/uploadEpub
const multer = require('multer')
exports.uploadEpub = (epubFile, coverFile) => {
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads")
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname.replace(/\s/g, ""))
}
})
const upload = multer({
storage
}).fields([{name: "bookFile", maxCount: 1},{name: "coverFile", maxCount: 1}])
}
controller/book
exports.addBook = async (req, res) => {
try {
const { ...data } = req.body
const newBook = await book.create({
...data,
bookFile: req.file,
coverFile: req.file
})
let bookData = await book.findOne({
where: {
id: newBook.id
},
attributes:{
exclude: ['createdAt','updatedAt']
}
})
bookData = JSON.parse(JSON.stringify(bookData))
res.send({
status: "Success",
Book: {
...bookData
}
})
} catch (error) {
console.log(error)
res.status(500).send({
status: "Failed",
message: "Server Error"
})
}
}
Multer set up
const multer = require('multer')
const path = require('path')
const { nanoid } = require('nanoid')
//Set Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: (req, file, callback) => {
const id = nanoid(6)
const newFilename = `${file.fieldname}_${id}-${new Date().toISOString().replace(/:/g, '-')}${path.extname(file.originalname)}`
callback(null, newFilename)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 5572864 }, // up to 5.5 MB
fileFilter: (req, file, callback) => {
checkFileType(file, callback)
},
})
//Check File Type
const checkFileType = (file, cb) => {
//Allowed extensions
const fileType = /jpeg|jpg|png|gif|svg|pdf|epub/
//Check extension
const extname = fileType.test(path.extname(file.originalname).toLowerCase())
//Check mimetype
const mimetype = fileType.test(file.mimetype)
if (extname && mimetype) {
return cb(null, true)
} else {
return cb('Error: wrong file type!')
}
}
module.exports = upload
/***** middlewares.js *****/
module.exports.imageUploader = (req, res, next)=>{
const files = req.files
const uploadedFiles = []
for (var i = 0; i <images.length; i++){
uploadFiles.push('https://yourserver.com/public/uploads/' + files[i].filename)
}
req.uploadFiles = uploadFiles.toString() //appending files to req
next()
return
}
/**** index.js or app.js ****/ //where your routes are defined
router.post('/books/add', upload.array('images', 10), imageUploader, book.addBook) // allowing up to 10 files to be uploaded, calling imageUploader as a middleware
/*** Controllers/book ***/
exports.addBook = async (req, res) => {
const uploadedFiles = req.uploadFiles; //catching files from imageUploader middleware
// ... rest of your code
}
Try to upload the file as form-data in postman, and put the key with the same name that you have set in multer, the postman will look like this:
Try this:
const cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
})
I'm struggling to attach metadata infos to a dfile before actually store it in mongoDB. I'm using:
const express = require("express");
const router = express.Router();
//File managing with gridfs
const mongoose = require('mongoose');
const path = require('path');
const crypto = require('crypto');
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const Grid = require('gridfs-stream');
const fs = require('fs');
As libraries, and then for the store engine :
const conn = mongoose.createConnection(mongoURI);
//TODO : test the api for the files
let gfs;
conn.once('open', () => {
// Init stream
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploads');
});
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
gfs:gfs,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
},
metadata: (req, file, cb) => {
const metadata = {
originalname: file.originalname,
// get this information somehow
restaurantID :req.query.restaurantID,
category : req.query.category
};
cb(null, metadata);
}
});
const upload = multer({ storage: storage });
Then for the post API i dont have that much yet just a code to update another mongoDB document with a reference to the file that works
router.post('/upload', upload.single("file"), (req, res) => {
//All the code to update
}
What I need is a way to store infos passed in req.query and put it into the metadata of the gridFS file collection in order to make some query to find the right files in the future and then save it obviusoly. This for every file that I upload with this API. Thank you guys.
I am using this code to add some information to the metadata of the fs.files collection.
state.storage = multerGridfs({
url: mongoServerUrl,
db: db.connection,
file: (req, file) => {
logger.info(`Uploading file: ${file.originalname} for account ${req.params.accountId}`);
return {
filename: file.originalname,
metadata: { accountId: req.params.accountId}
};
},
});
As you can see to use the metadata you return it as a property of the object you returned in the file: section.
Here is a link with some good examples https://www.npmjs.com/package/multer-gridfs-storage.
Hope this helps
I setup multer like this
let multer = require('multer');
let apiRoutes = express.Router();
let UPLOAD_PATH = '../uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, UPLOAD_PATH);
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now());
}
});
let upload = multer({ storage: storage });
and in route I am getting data and an image and use multer like!
apiRoutes.post('/update', passport.authenticate('jwt', { session: false }), (request, response) => {
let record = {
name: request.body.name,
location: request.body.location,
about: request.body.about,
userid: request.body.userid,
avatar: request.body.filename
};
let userData = {
name: request.body.name
};
if (request.body.filename) {
upload(request, response, (error) => {
});
}
profile.findOneAndUpdate({ userid: request.body.userid }, record, {new: true}, (error, doc) => {
if (error) response.json(error);
user.findOneAndUpdate({ _id: request.body.userid }, record, (error, result) => {
if (error) throw error;
response.json(doc);
});
});
});
What is happening with this code is that when I do not send an image to backend then I get data from front end and store it into database. But when I send image along side data then it return POST /api/1.0/profile/update 401 0.396 ms - -.
It means I am not getting any data at all. Whats wring with the code here?
You can't use Multer in your /update route. Use Multer in your router like this:
var upload = multer({ dest: 'uploads/' })
apiRoutes.post('/profile', upload.single('image'), function (req, res, next) {
// Uploaaded
})
if you add it and still can't get our file, you should update your form with this parameter: enctype="multipart/form-data"
I have this express route with multer file-upload. When the upload is complete, I would like to encode the image to base64 and send with response.
However when I do it like this, the code tries to execute the base64 encoding before the file is created to the folder.
Edit: Added storage & upload functions
const storage = multer.diskStorage({
destination: (req, file, callback) => {
if (!fs.existsSync('./uploads')) {
fs.mkdirSync('./uploads');
}
let path = './uploads';
callback(null, path);
},
filename(req, file, cb) {
let fileExt = file.originalname.substring(file.originalname.lastIndexOf('.')).toLowerCase();
if (!imageFilter(fileExt)) {
return false;
} else {
cb(null, file.originalname);
}
},
onError: function (err, next) {
console.log('error', err);
next(err);
},
});
const upload = multer({
storage,
limits: {
fileSize: 1000 * 1000 * 2 // 2 MB
}
}).single('file');
router.post('/upload', function (req, res) {
var directory = 'uploads';
fs.readdir(directory, (err, files) => {
if (err) throw err;
for (var file of files) {
fs.unlink(path.join(directory, file), err => {
if (err) throw err;
});
}
});
upload(req, res, function (err) {
if (err) {
return res.status(404).json({
success: false,
message: 'File is too large. (Max 2MB)'
});
}
var file = req.file;
var base64str = base64_encode('./uploads/' + file.originalname);
return res.status(200).json({
success: true,
url: 'http://' + ip.address() + ':' + constants.PORT + '/api/uploads/' + file.originalname,
image: 'data:image/png;base64,' + base64str
});
});
});
What would be the smartest way to achieve the right order of operations. Possibly promises or async/await?
This solution worked for me :
Node v8.4.0 is required for this
//app.js
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
app.use(cors({credentials: true, origin: 'http://localhost:4200'}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const Uploader = require('./Uploader.js');
const uploader = new Uploader();
app.post('/upload', uploader.startUpload);
//Uploader.js
const util = require("util");
const crypto = require("crypto");
const multer = require('multer');
class Uploader {
constructor() {
const storageOptions = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, __dirname + '/uploads/')
},
filename: function(req, file, cb) {
crypto.pseudoRandomBytes(16, function(err, raw) {
cb(null, raw.toString('hex') + Date.now() + '.' + file.originalname);
});
}
});
this.upload = multer({ storage: storageOptions });
}
async startUpload(req, res) {
let filename;
try {
const upload = util.promisify(this.upload.any());
await upload(req, res);
filename = req.files[0].filename;
} catch (e) {
//Handle your exception here
}
return res.json({fileUploaded: filename});
}
}
Edit :
The library "util" provide you a "promisify" method which will give you the possibility to avoid something called the "callback hell". It converts a callback-based function to a Promise-based one.
This is a small example to understand my code above:
const util = require('util');
function wait(seconds, callback) {
setTimeout(() => {
callback();
}, seconds);
}
function doSomething(callType) {
console.log('I have done something with ' + callType + ' !');
}
// Default use case
wait(2 * 1000, () => {
doSomething('callback');
});
const waitPromisified = util.promisify(wait);
// same with promises
waitPromisified(2000).then((response) => {
doSomething('promise');
}).catch((error) => {
console.log(error);
});
// same with async/await
(async () => {
await waitPromisified(2 * 1000);
doSomething('async/await');
})();
Will print :
I have done something with callback !
I have done something with promise !
I have done something with async/await !