Express multer middleware blocks request on data execution - node.js

I need to get the request progress AS a file is uploaded.
If I do
app.post('/upload', upload.single('file'), (req,res,next) => {
req.on('data', ()=> {
//DO SOMETHING
)
//PROCESS MY FILE
})
LOCALLY AND ON AWS EC2:
the "DO SOMETHING" method is only executed AFTER the multer middleware has done receiving all the file, so it doesn't work
So what I'm doing instead is:
app.post('/upload', (req,res,next) => {
req.on('data', ()=> {
//DO SOMETHING
)
next()
}, upload.single('file'), (req, res, next) => {
//PROCESS MY FILE
})
LOCALLY:
This works and "DO SOMETHING" is called whenever a chunk of file is uploaded
ON AWS EC2
This is not working. All the "DO SOMETHING" are called at the same time after the multer middleware is done receiving the file.
Why is it behaving this way?
How do I get it to correctly fire the events?
PS: I tried placing the next() call inside the data event once the file upload has completed, but then multer throws an exception
Error: Unexpected end of form at Multipart._final

Related

Is there any way to serve binary file input(stream?) directly to multer middleware(for unit test purpose)?

I'm trying to write test codes on multer and my service.
As multer is an express's middleware, multer is intended to process the request from client and automatically handle the file and put it in req.file.
But I wonder if there is any way without making real HTTP request, to pass binary file or stream to multer middleware so that I can check whether the file is saved in local disk properly or the file's size is put afterward in req.file.
My code as middleware module of multer is below, just for reference:
import { Request, Response, NextFunction } from 'express';
import multer from 'multer';
const multerMiddleware = (dest: string) => (req: Request, res: Response, next: NextFunction) => {
const upload = multer({
storage: multer.diskStorage({
destination: dest,
filename: (req, file, cb) => {
const { videoId } = req.body;
const filename = `${videoId}-${file.originalname}`;
cb(null, filename);
}
})
});
upload.single('file')(req, res, next);
}
export default multerMiddleware;
Or, is it the best way to do just make a test-purpose route in Express app and test with it? Like below:
app.post('/testMulter', multerMiddleware('temp/'), (req, res, next) => {
res.status(200);
res.json(req.file);
// ...
});
// in test codes
import request from 'supertest'
import App from '../app.ts'
it('multer middleware download files well', async (done) => {
const app = new App.app;
const response = await request(app)
.post('/testMulter')
.attach('file', './test.mp4');
expect(response.file.filename).toBe('test.mp4');
})
Thank you in advance.
So you want to do an integrational test but without HTTP stuff.
According to the provided code, only req.body is used. In terms of the unit testing, it is simple to mock such Request.
But we do not "know" how multer uses Request and Response. So if you still want to do an integrational test, you approach is good enough.
Overwise it is better to pass { body: { videoId: fakeId } } instead of Request and mock multer, multer.diskStorage, and and multer.single`.

Node Express "res.send is not a function" when used in middleware

My code looks like this:
const myMiddleware = () => {
return (req, res, next) => {
...
console.log(res);// this outputs only the locals set in a previous middleware
res.send('not working');//send is not a function
}
}
and its usage:
router.post('/route', previousMiddleware(), myMiddleware(), (req, res) => {
doSomeStuff;
})
The previous middleware does an https request, sets the results in the res.locals, then the myMiddleware does another request somewhere else using the data from the previous middleware. I need to exit out of the whole route if there is an error in the middleware. It works in the first (previous) middleware function, but not in the second one. When I log out res in the previous middleware, even after setting the locals, it gives me the whole object with the .send method. If I log the res on the first line of the myMiddleware() function (or anywhere), res is an object with a single object property thats just the .locals from the previous middleware:
{ locals: { key: value } }
I have triple checked and I do not have the order of req, res, next mixed up.
Any thoughts as to why, and how I can fix?
You should pass their names instead call them directly in router.
router.post('/route', previousMiddleware, myMiddleware, (req, res) => {
doSomeStuff;
})
Because that middleware will be call when a request is send to server, not when server is initialized.

Node+Express pipe file directly to writeStream

In terms of learning i need to make a file uploading by myself with fs.streams
router.post('/upload', (req, res, next) => {
req.pipe(fs.createWritableStream('files/file.png'))
.on('error', (err) => next(err))
.on('close', () => {
res.json({image: 'files/file.png'});
})
})
This is dosen't work. So two questions
How to get file name and data from req?
How to connect this two streams?
Update: In all tutorials described opposite action - read file from fs and pipe it to res to enduser.
you can use multer
const multer = require('multer');
const upload = multer({
dest: 'folderName'
});
router.post('/upload', upload.single('formField'), (req, res, next) => {
res.json({image: req.file.path});
})
in this code file path will be sent.
if you want to send file itself, then you should write:
res.download(req.file.path);
instead of res.json({image: req.file.path}).
I don't know how res.download works exactly. maybe it takes whole buffer of the file and sends it.
multer's documentation is here

receiving file in node-express uploaded with xhr

I have a xmlhttprequest which uploads the file and I am trying to receive it in my node-express server. but for some reason I am not able to retrieve the file content in the server. Not sure where I am missing it.
app.post('/api/uploadfiles', function(req, res) {
console.log("apicalled");
console.log(req);
console.log(req.body);
console.log(req.files);
console.log(JSON.stringify(req.files));
});
In order for you to see the files, you will need to add another middleware that parses multi-part request.
Try using connect-multiparty module like so:
var multipart = require('connect-multiparty'); //for files upload
var multipartMiddleware = multipart();//for files upload
app.post('/api/uploadfiles', multipartMiddleware, function(req, res) {
console.log("apicalled");
console.log(req);
console.log(req.body);
console.log(req.files);
console.log(JSON.stringify(req.files));
});

Express and uploading files

I am trying to upload files using express and formidable (eventualy forwarding to MongoDB and GridFS). I am starting by creating a form with a field of type file. On the action of that field I use the following route....
exports.addItem = function(req, res, next){
var form = new formidable.IncomingForm(),
files = [],
fields = [];
form
.on('file', function(field, file) {
console.log(field, file);
})
.on('end', function() {
console.log('-> upload done');
});
}
Everything runs fine but when I post I don't see anything in the console and it hangs.
The route looks like the following...
app.post('/item/add', routes.addItem, routes.getPlaylist, routes.index)
Any ideas?
UPDATE
Here is an example of grabbing the file, however, this still doesn't include formidable...
https://gist.github.com/2963261
The reason it is hanging is because you need to call next() to tell Express to continue.
Also use the bodyParser() middleware in express (included by default) to get the files. Something like this:
exports.addItem = function(req, res, next){
if(req.files.length > 0)
{
// process upload
console.log(req.files);
}
next();
}

Resources