Let’s start using formidable
The package works by creating an instance of the form data that is coming from the client-side. It will be an object instance with some default key-value pairs that we can change following our requirements.
To start using formidable we need a form:
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="myFile" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
Change the value of the action attribute in the form to /api/upload
and set enctype is multipart/form-data
.
const form = new formidable.IncomingForm();
console.log(form);
If you upload something from your rendered web page after restarting your server, you should see something like this on your terminal.
IncomingForm {
"options": {
"maxFields": 1000,
"maxFieldsSize": 20971520,
"maxFiles": null,
"maxFileSize": 209715200,
"maxTotalFileSize": 209715200,
"minFileSize": 1,
"allowEmptyFiles": false,
"createDirsFromUploads": false,
"keepExtensions": false,
"encoding": "utf-8",
"hashAlgorithm": false,
"uploadDir": "C:\\Users\\hphuc\\AppData\\Local\\Temp",
"enabledPlugins": [
null,
null,
null,
null
],
"fileWriteStreamHandler": null,
"defaultInvalidName": "invalid-name"
},
...
}
As you can see, in the options
fields we have many properties With these properties, we can configure formidable in our specification. Now let’s change the configuration by altering a few properties in the form instance:
const form = new formidable.IncomingForm({
uploadDir: 'uploads/',
maxFileSize: 100 * 1024 * 1024, // 10MB
keepExtensions: true,
filename: function (name, ext, part, form) {
const { originalFilename, mimetype } = part;
return originalFilename;
},
filter: function ({ name, originalFilename, mimetype }) {
// keep only images
const valid = mimetype?.includes('image');
if (!valid)
form.emit('error', new formidableErrors.default('Invalid file', 0, 400));
return valid;
},
});
filename
name
: basename of the http originalFilenameext
: with the dot ".png" only if keepExtensions is truepart
: where part can be decomposed asconst {originalFilename, mimetype} = part
filter
- use
form.emit('error')
to makeform.parse
error.
After that, we need to add this piece of code below the configuration.
form.parse(req, (err, fields, files) => {
if (err) {
res.status(400).json({
err,
message: err.message,
});
return;
}
// Upload to cloudinary or aws s3
return res.status(200).json({ message: ' File Uploaded ' });
});
parse(request, callback)
The “parse” function parses an incoming Node.js request containing form data. If a callback is provided, all fields and files are collected and passed to the callback.
We aim to parse and store these files according to our own needs, thus we need to take a look at them before we work on them.
"fields": {
"title": [
""
]
},
"files": {
"myFiles": [
{
"size": 101415,
"filepath": "uploads\\invalid-name",
"newFilename": "4d95a77cdd086c0da880a7701",
"mimetype": "image/jpeg",
"mtime": "2023-07-10T01:47:57.772Z",
"originalFilename": "32394441.jpg"
}
]
}
The fields
object has title
because in html file we have input tag name title
.
Since the user can upload multiple files at once, the incoming parsed data will be an array of objects. Data with multiple files looks something similar to this.
{
myFiles: [
{....},
{....}
]
}
Here is the whole upload function for reference purposes.
const express = require('express');
const formidable = require('formidable');
const formidableErrors = require('formidable').errors;
const app = express();
app.get('/', (req, res) => {
res.send(`
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="myFiles" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
`);
});
const uploadFile = (req, res, next) => {
const form = new formidable.IncomingForm({
uploadDir: 'uploads/',
maxFileSize: 100 * 1024 * 1024, //10MB
keepExtensions: true,
filename: function (name, ext, part, form) {
const { originalFilename, mimetype } = part;
return originalFilename;
},
filter: function ({ name, originalFilename, mimetype }) {
// keep only images
const valid = mimetype?.includes('image');
if (!valid)
form.emit(
'error',
new formidableErrors.default('Invalid type', 0, 400)
);
return valid;
},
});
form.parse(req, (err, fields, files) => {
if (err) {
res.status(400).json({
err,
message: err.message,
});
return;
}
// Upload to cloudinary or aws s3
return res.status(200).json({ message: ' File Uploaded ' });
});
};
app.post('/api/upload', uploadFile);
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000');
});