A node.js module for parsing incoming HTML form data.
Changes (breaking or otherwise) in v1.0.0 can be found here.
- node.js -- v10.16.0 or newer
npm install busboy
- Parsing (multipart) with default options:
const http = require('http');
const busboy = require('busboy');
http.createServer((req, res) => {
if (req.method === 'POST') {
console.log('POST request');
const bb = busboy({ headers: req.headers });
bb.on('file', (name, file, info) => {
const { filename, encoding, mime } = info;
console.log(
`File [${name}]: filename: %j, encoding: %j, mime: %j`,
filename,
encoding,
mime
);
file.on('data', (data) => {
console.log(`File [${name}] got ${data.length} bytes`);
}).on('close', () => {
console.log(`File [${name}] done`);
});
});
bb.on('field', (name, val, info) => {
console.log(`Field [${name}]: value: %j`, val);
});
bb.on('close', () => {
console.log('Done parsing form!');
res.writeHead(303, { Connection: 'close', Location: '/' });
res.end();
});
req.pipe(bb);
} else if (req.method === 'GET') {
res.writeHead(200, { Connection: 'close' });
res.end(`
<html>
<head></head>
<body>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="filefield"><br />
<input type="text" name="textfield"><br />
<input type="submit">
</form>
</body>
</html>
`);
}
}).listen(8000, () => {
console.log('Listening for requests');
});
// Example output:
//
// Listening for requests
// < ... form submitted ... >
// POST request
// File [filefield]: filename: "logo.jpg", encoding: "binary", mime: "image/jpeg"
// File [filefield] got 11912 bytes
// Field [textfield]: value: "testing! :-)"
// File [filefield] done
// Done parsing form!
- Save all incoming files to disk:
const { randomFillSync } = require('crypto');
const fs = require('fs');
const http = require('http');
const os = require('os');
const path = require('path');
const busboy = require('busboy');
const random = (() => {
const buf = Buffer.alloc(16);
return () => randomFillSync(buf).toString('hex');
})();
http.createServer((req, res) => {
if (req.method === 'POST') {
const bb = busboy({ headers: req.headers });
bb.on('file', (name, file, info) => {
const saveTo = path.join(os.tmpDir(), `busboy-upload-${random()}`);
file.pipe(fs.createWriteStream(saveTo));
});
bb.on('close', () => {
res.writeHead(200, { 'Connection': 'close' });
res.end(`That's all folks!`);
});
req.pipe(bb);
return;
}
res.writeHead(404);
res.end();
}).listen(8000, () => {
console.log('Listening for requests');
});
busboy
exports a single function:
( function )(< object >config) - Creates and returns a new Writable form parser stream.
-
Valid
config
properties:-
headers - object - These are the HTTP headers of the incoming request, which are used by individual parsers.
-
highWaterMark - integer - highWaterMark to use for the parser stream. Default: node's stream.Writable default.
-
fileHwm - integer - highWaterMark to use for individual file streams. Default: node's stream.Readable default.
-
defCharset - string - Default character set to use when one isn't defined. Default:
'utf8'
. -
preservePath - boolean - If paths in filenames from file parts in a
'multipart/form-data'
request shall be preserved. Default:false
. -
limits - object - Various limits on incoming data. Valid properties are:
-
fieldNameSize - integer - Max field name size (in bytes). Default:
100
. -
fieldSize - integer - Max field value size (in bytes). Default:
1048576
(1MB). -
fields - integer - Max number of non-file fields. Default:
Infinity
. -
fileSize - integer - For multipart forms, the max file size (in bytes). Default:
Infinity
. -
files - integer - For multipart forms, the max number of file fields. Default:
Infinity
. -
parts - integer - For multipart forms, the max number of parts (fields + files). Default:
Infinity
. -
headerPairs - integer - For multipart forms, the max number of header key-value pairs to parse. Default:
2000
(same as node's http module).
-
-
This function can throw exceptions if there is something wrong with the values in config
. For example, if the Content-Type in headers
is missing entirely, is not a supported type, or is missing the boundary for 'multipart/form-data'
requests.
-
file(< string >name, < Readable >stream, < object >info) - Emitted for each new file found.
name
contains the form field name.stream
is a Readable stream containing the file's data. No transformations/conversions (e.g. base64 to raw binary) are done on the file's data.info
contains the following properties:-
filename
- string - If supplied, this contains the file's filename. WARNING: You should almost never use this value as-is (especially if you are usingpreservePath: true
in yourconfig
) as it could contain malicious input. You are better off generating your own (safe) filenames, or at the very least using a hash of the filename. -
encoding
- string - The file's'Content-Transfer-Encoding'
value. -
mimeType
- string - The file's'Content-Type'
value.
Note: If you listen for this event, you should always consume the
stream
whether you care about its contents or not (you can simply dostream.resume();
if you want to discard/skip the contents), otherwise the'finish'
/'close'
event will never fire on the busboy parser stream. However, if you aren't accepting files, you can either simply not listen for the'file'
event at all or setlimits.files
to0
, and any/all files will be automatically skipped (these skipped files will still count towards any configuredlimits.files
andlimits.parts
limits though).Note: If a configured
limits.fileSize
limit was reached for a file,stream
will both have a boolean propertytruncated
set totrue
(best checked at the end of the stream) and emit a'limit'
event to notify you when this happens. -
-
field(< string >name, < string >value, < object >info) - Emitted for each new non-file field found.
name
contains the form field name.value
contains the string value of the field.info
contains the following properties:-
nameTruncated
- boolean - Whethername
was truncated or not (due to a configuredlimits.fieldNameSize
limit) -
valueTruncated
- boolean - Whethervalue
was truncated or not (due to a configuredlimits.fieldSize
limit) -
encoding
- string - The field's'Content-Transfer-Encoding'
value. -
mimeType
- string - The field's'Content-Type'
value.
-
-
partsLimit() - Emitted when the configured
limits.parts
limit has been reached. No more'file'
or'field'
events will be emitted. -
filesLimit() - Emitted when the configured
limits.files
limit has been reached. No more'file'
events will be emitted. -
fieldsLimit() - Emitted when the configured
limits.fields
limit has been reached. No more'field'
events will be emitted.