Express.js - File Handling
npm install --save multerMulter parses incoming requests for files, so it can parse text and also files.
Storing files
views/product-add.js
<form action="/admin/product-add" enctype="multipart/form-data" method="post">
    <input type="file" name="image" id="image" />
</form>routes/admin.js
const path = require('path');
const express = require('express');
const router = express.Router();
const productsController = require('../controllers/shop');
const isAuth = require('../middleware/is-auth');
const products = [];
router.get('/product-add', isAuth, productsController.getAddProduct);
router.post('/product-add', isAuth, productsController.postAddProduct);controllers/shop.js
exports.postAddProduct = (req, res, next) => {
    const title = req.body.title;
    const image = req.file;
    const price = req.body.price;
    const description = req.body.description;
    if (!image) {
        return res.status(422).render('admin/edit-product', {
            hasError: true,
            errorMessage: 'Attached file is not an image'
        });
    }
    ...
        store in db
    ...
}app.js
...
const multer = require('multer');
const fileStorage = multer.diskStorage({
    destination: (req, file, callback) => {
        callback(null, 'images');
    },
    filename: (req, file, callback) => {
        callback(null, new Date().toISOString() + '-' + file.originalname);
    }
});
const fileFilter = (req, file, callback) => {
    if (file.mimetype == 'image/png' ||
        file.mimetype == 'image/jpg' ||
        file.mimetype == 'image/jpeg'
    ) {
        callback(null, true);
    } else {
        callback(null, false);
    }    
}
app.use(multer
    ({
        storage: fileStorage,
        fileFilter: fileFilter
    })
    .single('image'));  //we are expecting single file with name image
app.use('/images', express.static(path.join(__dirname, 'images'))); //serve images as static filesServing files
Uploaded images can be served as public static files. But for example invoices we want to serve only for users which it belongs to. We will also generate PDF file with pdfkit.
npm install --save pdfkitroutes/shop.js
...
router.get('orders/:orderId', isAuth, shopController.getInvoice);
...controllers/shop.js
const fs = require('fs');  //file system
const path = require('path');
const Order = require('../models/order');
const PDFDocument = require('pdfkit');
exports.getInvoice = (req, res, next) => {
    const orderId = req.params.orderId; 
    Order.findById(orderId).then(order => {
        if (!order) {
            return next(new Error('No order found'));
        }
        if (order.user.userId.toString() !== req.user._id.toString()) {
            return next(new Error('Unauthorizes'));
        }
        const invoiceName = 'invoice-' + orderId + '.pdf';
        const invoicePath = path.join('data', 'invoice', invoiceName);
       
        /*
        //readFile is good only for small files, because node read content of whole file into memory. when there are lot of request, this can cause problems
        fs.readFile(invoicePath, (err, data) => {
            if (err) {
                return next(err);
            }
            res.setHeader('Content-Type', 'application/pdf');
            res.setHeader('Content-Disposition', 'attachment; filename="' + invoiceName + '"');
            res.send(data);
        });
        */
        //streaming is good for bigger files
         
        const pdfDoc = new PDFDocument();
        res.setHeader('Content-Type', 'application/pdf');
        res.setHeader('Content-Disposition', 'attachment; filename="' + invoiceName + '"');
        pfdDoc.pipe(fs.createWriteStream(invoicePath));
        pdfDoc.pipe(res);
        pdfDoc.text('Hello this is test invoice');
        pdfDoc.end();
    })
    .catch(err => next(err));
};