I need to set the file upload path based on form field values in a formidable form upload that has both the file and fields ( multipart form type).
theForm.on('fileBegin', function (name, file){ ...}
is called before
theForm.parse(req, function(err, fields, files) { ... }
However, it seems that the form upload path must be set before the form fields are parsed. And so I don't see a way to access properties in fields yet. Theres nothing there yet except for prototype. I also looked in req.body but the values aren't there either.
Is this correct? Is there a way to change the form upload path after the form fields are available, but before the file is then saved to disk?
I am using the most current of everything as of today, 7/31/2017.
I also have body-parser in use, to read in JSON.. Could that be causing this issue? ( I've read that it is and also is NOT okay to use it- removing it causes other issues for me, so I've left it in so far .. )
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
Many thanks!
I would suggest to use the express-fileupload module:
var express = require('express');
var app = express();
var path = require('path');
const fileUpload = require('express-fileupload');
app.use(fileUpload());
var defaultDir = "C:\\temp";
app.post('/upload', function(req, res){
// The name of the input field (i.e. "clientfile") is used to retrieve the uploaded file
let sampleFile = req.files.clientfile;
var savefile_path = path.join(defaultDir, req.body.path, sampleFile.name);
// Use the mv() method to place the file somewhere on your server
sampleFile.mv(savefile_path, function(err) {
if (err)
{
return res.status(500).send(err);
}
});
res.status(200).send("File uploaded");
console.log("File uploaded");
});
Related
I have multer as middleware before editing user function. The thing is that multer uploads photo no matter what, so I am wondering if there is a way to somehow cancel upload if e.g. email is invalid. I tried to delete uploaded image through function via fs.unlink if there is validation error within edit function, but I get "EBUSY: resource busy or locked, unlink" error. I guess that multer uploads at the same time while I try to delete image.
Any ideas how to solve this?
on your function make a try/catch block and handle on error throw
import { unlink } from 'node:fs/promises';
import path from 'path'
// code ...
// inside your function
const img = req.file // this needs to be outside the try block
try {
// your code, throw on failed validation
} catch (e) {
if (img) {
// depends on where you store in multer middleware
const img_path = path.resolve(YOUR_PATH, img.filename)
await unlink(img_path);
console.log(`deleted uploaded ${ img_path }`);
}
// revert transaction or anything else
}
Nowadays, applications usually separates uploading file API from data manipulating API for some features like previewing/editing image. Later, they can run a background job to clean unused data.
But if it's necessary in your case, we can use multer's builtin MemoryStorage to keep file data in memory first, then save it to disk after validation completes.
const express = require('express');
const app = express();
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
const fs = require('fs');
app.post("/create_user_with_image", upload.single('img'), (req, res) => {
// Validation here
fs.writeFile(`uploads/${req.file.originalname}`, req.file.buffer, () => {
res.send('ok');
});
});
Note: as multer documentation said, this solution can cause your application to run out of memory when uploading very large files, or relatively small files in large numbers very quickly.
I am trying to get the image when user submits the form and inserting it into mongoDB server.For image I am using Multer plugin but its showing me the error.Here is my code of NodeJS
const multer = require('multer');
mongoose.connect('mongodb://localhost:27017/mytable',{useNewUrlParser:true} )
.then(()=>
console.log("Mongodb connected"))
.catch(err => console.error("could not connected",err));
const Schema =new mongoose.Schema({
name:String,
email:String,
lastname:String,
pass:String,
phonenumber:String,
zipcode:String,
birthdate:String,
img: {contentType:String,data:Buffer }
});
Schema.plugin(mongoosePaginate)
var user = mongoose.model('mytable', Schema);
//Multer for include image into directory
app.use(multer({ dest: '/public/'}).single('files'));
app.post('/save',(req,res)=>{
console.log("image is" +req.body.img);
var model = new user();
model.name = req.body.name,
model.email=req.body.email,
model.lastname=req.body.lastname,
model.pass=req.body.pass,
model.phonenumber=req.body.phonenumber,
model.zipcode=req.body.zipcode,
model.birthdate=req.body.birthdate,
/* model.img.data = req.body.img, */
model.img.data = fs.readFileSync(req.files.userPhoto.path);
newPic.image.contentType = 'image/png';
model.save(function(err,doc){
});
res.json({result:'sucess'});
res.end();
});
I just uploaded the required code. I am getting the error of Cannot read property 'userPhoto' of undefined .I don't know what should I write in fs.readFilesync.Please help me to insert image into a server .
You ask Multer to handle a .single() file that is expected to be referred to by the input name "files". According to the doc:
The single file will be stored in req.file
But you try to access req.files instead. (And it seems you're expecting this file to be referred to as "userPhoto", maybe?).
See also what information Multer exposes to retrieve the uploaded file's path.
Finally, you might want to restrict your middleware usage to the routes that need it.
EDIT: a few comments
// This tells the WHOLE app that:
// when a request comes in, execute this
app.use(
// Multer will do:
// when I'm given an incoming request (here it's every request)
// then I'm looking for *one* file being uploaded
// this file should be named "files" (i.e. client has <input type="file" name="files">)
// if I find it, then I store it in /public
multer({ dest: '/public/'}).single('files')
);
// This tells the app that:
// if a request comes in for endpoint /save with method POST, this is the code to execute
app.post('/save', (req, res) => {
// in here, Multer will have been executed already
});
So:
does your form really names its file to be uploaded "files"? I'd guess your form names the file "userPhoto"... just a guess!
if such a file exists in the request, Multer documentation says that your route handler can access it in req.file (not req.files)
if not, Multer will just let the request pass (that's what middlewares do), so you won't have a req.file
if req.file is mounted on the request by Multer, it exposes several data fields, such as req.file.path
I also hinted that you may not want to enable Multer for the whole app, but just for the routes that require it. Instead of a "global" app.use, you can define several times a route (or you could explicitly use the router, I don't see much of a difference), like:
app.post('/save', multer(...));
app.post('/save', (req, res) => {...});
// which can also be written as
app.post('/save', multer(...), (req, res) => {...});
This way, all other routes do not consider file uploading, I'm sure I don't need to highlight how better this is.
the problem is not with mongoose! as it says in your error message, req.files is undefined. it's a problem with multer documentation! when you're using single your file will be available in req.file
so this would fix your problem:
app.post('/save',(req,res)=>{
console.log("image is" +req.body.img);
var model = new user();
model.name = req.body.name,
model.email=req.body.email,
model.lastname=req.body.lastname,
model.pass=req.body.pass,
model.phonenumber=req.body.phonenumber,
model.zipcode=req.body.zipcode,
model.birthdate=req.body.birthdate,
/* model.img.data = req.body.img, */
model.img.data = fs.readFileSync(req.file.path); // here's the fix
newPic.image.contentType = 'image/png';
model.save(function(err,doc){
});
res.json({result:'sucess'});
res.end();
});
date: { type: Date, default: Date.now } // stick that into the Schema
I need to receive, in a single request, either JSON data and files. So I've been using body-parser which works perfect. However I'm having problems finding a module working nice with express.
This is my router setup:
router.post('/',
// controllers.requireAuthorization,
controllers.multipartMiddleware,
function (req, res) {
console.log(req.body);
return res.json({ body: req.body });
},
controllers.sitters.validate,
controllers.sitters.create,
controllers.sitters.serialize
);
This is what my multipart middleare function looks like, as you can see I'm using multiparty:
function multipartMiddleware(req, res, next) {
if (req.get('Content-Type').indexOf('multipart/form-data') + 1) {
new multiparty.Form().parse(req, function (err, fields, files) {
console.log(JSON.stringify(files));
req.body = fields;
return next(err);
});
} else {
console.log(req.get('Content-Type'));
return next();
}
}
Of course I've added that premature response return for debug purposes. So I need:
Receives files which will be streamed right away to S3.
Parse the rest of data as normal data.
Correctly get the data parsed
The issues are seeing right now:
It takes long sometimes even for small files (less than 512k, up to 5 seconds, this could be becase I'm using vagrant while developing but I think it's odd).
Fields are not parsed properly:
See how the value of loca is inside a wrapping array.
I would checkout multer, its a popular Express middleware and has a number of different ways to handle files (one or many). It also still allows for fields to be set which will come through on your req.body.
var multer = require('multer');
var upload = multer.dest({ 'temp/' });
// looking for single file, named 'file'
app.put('/file', upload.single('file'), function(req, res) {
// access the file
var file = req.file
// any form fields sent in addition to the file are here
var body = req.body;
});
Another popular package thats an alternative to multer is busboy. Its also worth noting that multer is written on top of busboy.
Is there any way to make some image returning if user accesses /images/avatars/thumbnails/ID.png and it doesn't exist? (for users without avatars)
There are a few ways to do this. The simplest that comes to mind is a route like:
// in config/routes.js
'/images/avatars/thumbnails/:thumb': 'AvatarController.show'
// controllers/AvatarController.js
var fs = require('fs');
show: function(req, res, next) {
var path = sails.config.appPath+'/assets/images/avatars/thumbnails/'+req.param('thumb');
fs.exists(path, function(exists) {
if (exists) return next();
}
fs.createReadStream(sails.config.appPath+'/assets/images/defaultAvatar.png').pipe(res);
}
This sets a custom action to handle requests for avatar thumbnails. If the file is found, it falls back to the default action for static assets (sending it to the client). If the file doesn't exist, it streams your default avatar file to the client.
you can use the node.js fs module
var fs = require('fs');
var imagePath = '/images/avatars/thumbnails/ID.png';
fs.open(imagePath, 'r', function(err,fd){
if(err) imagePath = 'path/to/custom/image.png';
else console.log('user image exists');
});
i have 3 files: server.js, bookhandler.js, books.js
i assume that there will be requests like: /book/name, /book/type, /book etc.
according to the first field in the url, which is book(it can also be video and magazine),i want my server.js to direct me to the bookhandler.js file with the post parameters. i only want a function here which i can validate the parameters. and if parameters are valid, i want to pass the parameters to my books.js file which i can make my post request with those valid parameters.if the first field of url is video, it should direct me to videohandler etc.
server.js
app.use('/book', require('./bookhandler').middleware);
this is what i did so far, and it directs me to the bookhandler file, but i dont know how to get parameters and validate inside a function
EDİT:
bookhandler.js
var express = require('express');
var app = express();
module.exports.middleware = function (req, res) {
var id = req.body.id;
if(id.length==5) {
app.use('/id', require('../book').middleware));
}
};
book.js
app.post('/id' , function (req, res) {
});
module.exports = app;
You don't direct execution to a file, but to a function. Files are just a way of organizing functions or any type of code.
Based on this example you gave, I assume that bookhandler.js exports a function named middleware in its module.exports object. That function, when called, will be passed a reference to the request and response objects. The request object has the parameters in it.
For example:
bookhandler.js:
module.exports.middleware = function (req, res) {
var bookId = req.params.bookId;
// do something with bookId
};
[Edit based on latest update of question]
You are over-riding module.exports. After setting module.exports.middleware to a valid function, you replace module.exports in its entirety with a reference to app.
Remove this line and your problem should be solved.
module.exports = app;