Node JS Image Delivery Microservice Challenge [Setup]

First of all we need node js server installed in our server.

Second of all we need to create the microservice server. We need to use typescript.

We take quick look here: https://developer.okta.com/blog/2018/11/15/node-express-typescript

We set up the project to use typescriptnpm install --save-dev typescript

We install tslintnpm install --save-dev tslint

We install expressnpm install express

The first running code looks like this:

import express from "express";
const app = express();
const port = 8080; // default port to listen

// define a route handler for the default home page
app.get("/", (req, res) => {
    res.send("Hello world!");
});

// start the Express server
app.listen(port, () => {
    // tslint:disable-next-line:no-console
    console.log(`server started at http://localhost:${port}`);
});

 

We start the server: npm run start

If we call http://localhost:8080/ with Postman we get Hello World.

For the image pth we add this piece of code:

// define a route handler for the image processing
app.get("/image/:imageName/:imageResolution", (req, res) => {
    res.send(`image named ${req.params.imageName} and resolution ${req.params.imageResolution}`);
});

 

We call it with Postman:

For caching we might use info from here:
https://medium.com/the-node-js-collection/simple-server-side-cache-for-express-js-with-node-js-45ff296ca0f0

Cache will not be used right now. We will focus on image serving and image resizing.

For image resizing we use this article:

https://gabrieleromanato.name/nodejs-resize-images-as-thumbnails-on-the-fly-in-expressjs

This means that we are using imagemagick and graphicsmagick libraries.

We install gm npm install gm

Examples of usage here: https://www.npmjs.com/package/gm

Maybe use this for streaming and caching https://stackoverflow.com/questions/39232296/node-js-cache-image-to-filesystem-and-pipe-image-to-response?rq=1

We try using stream for streaming: npm install stream

We use path for file name manipulation

const fileExtension = path.extname(fileAbsolutePath);
const fileName = path.basename(fileAbsolutePath).replace(fileExtension, "");

 

GM not working, mabe use sharphttps://github.com/lovell/sharp

We uninstall the old packages gm and imagemagick.

We install new sharp package npm install sharp

We use sharp to resize the image, save it to the folder and send it to the user.

 sharp(fileAbsolutePath).resize(100, 100).toFile(resizeFileAbsolutePath, (err, info) => {
                if (err) {
                    // tslint:disable-next-line:no-console
                    console.error(err);
                    res.status(404).send({ error: err });
                } else {
                    res.status(200).sendFile(resizeFileAbsolutePath);
                }
            });

 

We finally get a barely working image processor with about 30 lines of code.

// define a route handler for the image processing
app.get("/image/:imageName/:imageResolution", (req, res) => {
    // res.send(`image named ${req.params.imageName} and resolution ${req.params.imageResolution}`);
    const imageName = req.params.imageName;
    const imageResolution = req.params.imageResolution;
    const imagesFolder = __dirname + `/images/`;
    const fileAbsolutePath = imagesFolder + `${imageName}`;
    const fileExists = fs.existsSync(fileAbsolutePath);
    if (fileExists) { // file with requested resolution already exists
        const resolutionExists: boolean = false; // to see how to organize file and check for different resolutions
        if (resolutionExists) {// If the image does exist on the file system, then stream the image to the response object
            fs.createReadStream(fileAbsolutePath).pipe(res);
        } else { // file with required resolution does not exist, we must resize it
            const fileExtension = path.extname(fileAbsolutePath);
            const fileName = path.basename(fileAbsolutePath).replace(fileExtension, "");
            const resizedFileName = `${fileName}_100x100${fileExtension}`;
            const resizeFileAbsolutePath = imagesFolder + `${resizedFileName}`;
            
            sharp(fileAbsolutePath).resize(100, 100).toFile(resizeFileAbsolutePath, (err, info) => {
                if (err) {
                    // tslint:disable-next-line:no-console
                    console.error(err);
                    res.status(404).send({ error: err });
                } else {
                    res.status(200).sendFile(resizeFileAbsolutePath);
                }
            });

            
        }
    } else {
        res.status(404).send({ error: `image named ${imageName} and resolution ${imageResolution} was not found` });
    }
});

 

If we call the url with POSTMAN we get the image created on the server and the image served. (24ms)

 

Leave a Reply

Your email address will not be published. Required fields are marked *