Node Busboy abort upload
Asked Answered
S

5

14

I'm using busboy, writing my uploaded file to a buffer and performing some validation on it (width, height and filesize). I can't for the life of me figure out how to abort / stop the stream once I find something wrong with the upload.

For instance if I have a max filesize of 500kb that I want to allow, I keep track of the size of the buffer as it's uploading and I want to abort if the size is over 500kb.

Here's a simplified version of my code.

var self = this;
var busboy = new Busboy({
    headers: self.req.headers,
    limits: {
        files: 1
    }
});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {

    file.fileRead = [];
    var size = 0;
    file.on('data', function(chunk) {

        size += chunk.length;

        /* DO VALIDATION HERE */
        if( size > 500000) {
            /*** ABORT HERE ***/
        }


        file.fileRead.push(chunk);
    });

    file.on('end', function() {
        var data = Buffer.concat(file.fileRead, size);
        // ... upload to S3
    });

    self.req.pipe(busboy);
});
Starknaked answered 26/5, 2015 at 18:12 Comment(5)
You can limit the file size as a parameter to busyboy - it's just a fileSize property...Boxhaul
What if I want to validate the width and height of the file? I would need to abort the stream then, instead of counting on the limit event.Starknaked
I have a post about this somewhere in my accepted bounties but it's not using busbuy.Boxhaul
@BenjaminGruenbaum I'd love to see it if you dig it up. Post an answer and if it's what I need I will accept it.Starknaked
It performs the check on the header but can be very easily applied to do whatever you want it to https://mcmap.net/q/504883/-break-http-file-uploading-from-server-side-by-php-or-apacheBoxhaul
E
6

Ok, so I had the same problem and I solved it with file.resume();

var fstream;
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {

    // Validate file mimetype
    if(mimetype != 'image/png'){
        file.resume();
        return res.json({
            success: false,
            message: 'Invalid file format'
        });
    }

    // Upload
    fstream = fs.createWriteStream(__dirname + '/tmp/' + timestamp + filename);
    file.pipe(fstream);
    fstream.on('close', function () {
        return res.json({
            success: true
        });
    });

});

req.pipe(req.busboy);
Engracia answered 26/8, 2015 at 9:29 Comment(1)
This doesn't abort the upload it just waits until all data has been consumed.Plop
M
2

The answer is simple one.

// do the required validation like size check etc.
if( size > 500000) 
{
    self.req.unpipe();
    return;
}

The context is this. I see in busboy code that busboy is implemented as WritableStream and underneath uses Dicer module for parsing which is also implemented as WritableStream. Flow is like this:

req stream ==> busboy ==> dicer ==> dicer raises events ==> busboy raises events on file ==> these events are consumed in your code.

To stop this whole flow of code - the above unpipe should do.

Medan answered 4/6, 2015 at 6:21 Comment(0)
A
1

I would try something like this:

var self = this;
var busboy = new Busboy({
    headers: self.req.headers,
    limits: {
        files: 1,
        fileSize: 500000
    }
});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {

    file.fileRead = [];

    file.on('limit', function() {
        //Clear variables
    });

    file.on('data', function(chunk) {
        file.fileRead.push(chunk);
    });

    file.on('end', function() {
        var data = Buffer.concat(file.fileRead, size);
        // ... upload to S3
    });

    self.req.pipe(busboy);
});

Basically I added a new limit in the configuration of Busboy: fileSize: 500 * 1024

And I started listening limit event:

    file.on('limit', function() {
        //Clear vars
    });
Amitosis answered 29/5, 2015 at 19:43 Comment(3)
I'm not sure this makes sense to me. What variables would I clear in limit and why / how would that stop the stream? The ideas is I don't want to parse a 5gig file if I know after 200kb that it's the wrong filetype or size.Starknaked
I also should say that I am doing other validation, like the width and height of the file.Starknaked
To abort the communication may be used: file.resume(); according to: github.com/mscdex/busboy/issues/22, github.com/mscdex/busboy/pull/23 & github.com/mscdex/busboy#busboy-special-events (at note)Amitosis
M
1

I would think the proper thing to do was to just close the socket and end the request

busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {

    file.fileRead = [];
    var size = 0;
    file.on('data', function(chunk) {

        size += chunk.length;

        /* DO VALIDATION HERE */
        if( size > 500000) {

            self.req.socket.end();
            self.res.end();

        }


        file.fileRead.push(chunk);
    });

    file.on('end', function() {
        var data = Buffer.concat(file.fileRead, size);
        // ... upload to S3
    });

    self.req.pipe(busboy);
});
Mauldin answered 5/6, 2015 at 3:31 Comment(0)
S
-1

I was able to access the underlying Dicer parser which has an ignore method which essentially stops uploading the file.

Here's how I did it: busboy._parser.parser._ignore()

It does seem very hackish, but it works and does exactly what I wanted.

Starknaked answered 10/6, 2015 at 14:14 Comment(1)
Functions starting with an underscore usually signal private methods that should not be accessed from outside the library. I would highly advice against this.Surfacetoair

© 2022 - 2024 — McMap. All rights reserved.