Dealing with files on Node server

Upload a file #

Create a form to upload a file #

<form action="/upload" enctype ="multipart/form-data" method="post">
<input type="file" class="file-input" name="photo">
<input type="submit" value="Submit" class="submit-button" >
</form>

enctype ="multipart/form-data"

enctype attribute defines the MIME type of the input and giving it a value of multipart/form-data allows a file <input /> to upload file data

Handle multipart data #

for that task you can use Multer npm package which is a middleware for handling multipart data

so what exactly Multer does? #

Multer adds two objects to the request:

Upload single file

router.post('/upload', upload.single('photo'), (request, response) => {
// request.file
// request.body
console.log(request.file);
if (req.fileValidationError)
return res.status(400).json({ error: req.fileValidationError });

return res.status(201).json({ success: true });
}

Output for console.log(request.file)

{
fieldname: 'photo',
originalname: 'photo name.PNG',
encoding: '7bit',
mimetype: 'image/png',
destination: 'api/uploads/',
filename: 'photo name.PNG',
path: 'api\\uploads\\photo name.PNG',
size: 1165007
}

Upload multi-files

router.post('/upload', upload.array('photo'), (request, response) => {
// request.files
// request.body
console.log(request.files);
if (req.fileValidationError)
return res.status(400).json({ error: req.fileValidationError });

return res.status(201).json({ success: true });
}

Output for console.log(request.file)

[
{
fieldname: 'photo',
originalname: '1.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: 'server/uploads/',
filename: '1.jpg',
path: 'server\\uploads\\1.jpg',
size: 252438
},
{
fieldname: 'photo',
originalname: '2.gif',
encoding: '7bit',
mimetype: 'image/gif',
destination: 'server/uploads/',
filename: '2.gif',
path: 'server\\uploads\\2.gif',
size: 1511827
}
]

How to use Multer? #

Create a new instance of Multer passing an object with some options:

const upload = multer({
fileFilter,
storage,
});

OH YAH!!! you did it you uploaded a file ๐Ÿค›

Here's the whole peace of magic!โœจ #

const express = require("express");
const multer = require("multer");

const app = express();
const port = 3000;

const storage = multer.diskStorage({
destination: 'api/uploads/',
filename: filename,
});

const upload = multer({
fileFilter,
storage,
});

function filename(request, file, callback) {
callback(null, file.originalname);
}

function fileFilter(request, file, callback) {
if (file.mimetype !== 'image/png') {
request.fileValidationError = 'Wrong file type';
callback(null, false, new Error('Wrong file type'));
} else {
callback(null, true);
}
}

app.post('/upload', upload.single('photo'), (request, response) => {
// request.file
// request.body
console.log(request.file);
if (req.fileValidationError)
return res.status(400).json({ error: req.fileValidationError });

return res.status(201).json({ success: true });
}

app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`)
});

Delete a file #

For that task we will need to access the file system using the built-in library fs in node

fs is full of cool methods to deal with file system.

To delete a file from file system we have two methods

For this task we will use unlink and we will use the built-in library promisify to make it return a promise instead of dealing with callbacks dahhhh

And to define the path of the image we can use path npm dependency

in my hierarchy the images are added to uploads directory

Now let's code

const fs = require('fs');
const { promisify } = require('util');
const path = require("path");

const unlinkAsync = promisify(fs.unlink);
const filesDirPath = path.resolve(__dirname, '/uploads');

app.delete("/:name", async(req, res) => {
const filePath = `${imagesDirPath}/${req.params.name}`;
await unlinkAsync(filePath);
return res.status(200).json({ success: true });
});

Cool! Now you're deleting files like a pro

but what if the file to delete doesn't exist on your file system, this will crash your app

So let's handle this

for that we can use fs.existsSync(path) that checks if the path exists or not

const fs = require('fs');
const { promisify } = require('util');
const path = require("path");

const unlinkAsync = promisify(fs.unlink);
const filesDirPath = path.resolve(__dirname, '/uploads');

app.delete("/:name", async(req, res) => {
const filePath = `${imagesDirPath}/${req.params.name}`;

if (!fs.existsSync(filePath) {
return res.status(404).json({ error: `Image doesn't exist!` });
}
await unlinkAsync(filePath);
return res.status(200).json({ success: true });
});

Now we can feel safe!!!

That was about deleting a single file. Now what about deleting a bunch of files asynchronously

First thoughts is looping through the set of images to delete and remove each one using unlink and it's the right way because their is no such a thing in fs to remove many files

so here you go

Let's assume we will receive the names of the images to delete in the request as a JSON like this

{
"images": [
"img1.jpg",
"img2.jpg",
"img5.gif",
"img4.jpg"
]
}

And we can access it using req.body

app.delete("/:name", async(req, res) => {
const filesNames = req.body.images;

filesNames.forEach( async(file) => {
const filePath = `${imagesDirPath}/${req.params.name}`;
if (!fs.existsSync(filePath) {
return res.status(404).json({ error: `Image doesn't exist!` });
}
await unlinkAsync(filePath);
});
return res.status(200).json({ success: true });
});

Okay! that code may seem fine but it has an issue

this code will always return success message no matter what

to continue later...

๐ŸŽ‰๐Ÿ’…โœจ

Oh Hi๐Ÿ‘‹ You made it this far cool! ๐Ÿ™Œ If you would love to share it somewhere here you GO. Also, I would ๐Ÿ’– to hear your feedback, so feel free to ping me on Twitter. or tag me if you wish. Bye for now. Keep Learning! ๐Ÿคž

Published