Continuation from http://justcodesnippets.durlut.ro/index.php/2019/12/07/node-js-image-delivery-microservice-challenge-folder-structure/
We create a cluster on https://cloud.mongodb.com/ for development purposes.
We install mongodb driver npm install mongodb –save
We create DbClient class that will hold DB operations
import mongodb from "mongodb"; export class DbClient { public incrementValue(imageFullName: string, incrementTimesServed: boolean, incrementTimesResized: boolean, incrementTimesDirectlyServed: boolean) { const client = this.getClient(); client.connect((err) => { const imagesCollection = this.getImagesCollection(client); // https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndUpdate/ imagesCollection.findOneAndUpdate( { imageName: imageFullName } , { $inc: { timesServed: incrementTimesServed ? 1 : 0 , timesResized: incrementTimesResized ? 1 : 0 , timesDirectlyServed: incrementTimesDirectlyServed ? 1 : 0 } }, { upsert: true } ); client.close(); }); } public getImagesCollectionHtml(): Promise<string> { // https://medium.com/thecodinghype/https-medium-com-thecodinghype-reading-from-mongodb-database-using-express-js-and-node-js-250ef8b9282a // https://stackoverflow.com/questions/35246713/node-js-mongo-find-and-return-data const client: mongodb.MongoClient = this.getClient(); return client.connect().then((connectedClient) => { return this.getImagesCollection(connectedClient); }).then((imagesCollection) => { return imagesCollection.find().toArray(); }).then(async (images) => { // write HTML output let output: string = "<html><header><title>Images</title></header><body>"; output += "<h1>List retrieved from DB</h1>"; output += '<table border="1">'; output += `<tr> <td><b>Name</b></td><td><b>Nr of served images</b></td><td><b>Nr of times resized</b></td><td><b>Nr of times directly served</b></td></tr>`; // process list images.forEach((image) => { output += `<tr><td>${image.imageName}</td><td>${image.timesServed}</td><td>${image.timesResized}</td><td>${image.timesDirectlyServed}</td></tr>`; }); // write HTML output (ending) output += "</table></body></html>"; console.log(output); await client.close(); return output; }); } private getImageStatsDb(client: mongodb.MongoClient): mongodb.Db { return client.db("imageStats"); } private getImagesCollection(client: mongodb.MongoClient): mongodb.Collection<any> { return this.getImageStatsDb(client).collection("images"); } private getClient(): mongodb.MongoClient { const uri = "mongodb+srv://admin:admin@cluster0-pyydy.mongodb.net/test?retryWrites=true&w=majority"; const client = new mongodb.MongoClient(uri, { useNewUrlParser: true }); return client; } }
As the images are being served we increment in the database the images values:
In the end, the index.ts file holds 1 more route and the images route also makes calls to the database.
import express from "express"; import { DbClient } from "./db/db-client"; import { RequestImageValidatorService } from "./services/request-image-validator-service"; import { ServedImageService } from "./services/served-image-service"; import { FolderStructureUtils } from "./utils/folder-structure-utils"; const app = express(); const port = 8080; // default port to listen app.use(express.static("images")); // https://expressjs.com/en/starter/static-files.html // define a route handler for the default home page app.get("/", (req, res) => { res.send("Hello world!"); }); // define a route handler for the image processing app.get("/image/:imageName/:imageResolution", async (req, res, next) => { // res.send(`image named ${req.params.imageName} and resolution ${req.params.imageResolution}`); const imageName = req.params.imageName; const imageResolution = req.params.imageResolution; console.log(`requested image named ${imageName} and resolution ${imageResolution}`); const imageValidatorService: RequestImageValidatorService = new RequestImageValidatorService(imageName, imageResolution); if (!imageValidatorService.validateImage()) { res.status(404).send({ errors: imageValidatorService.errors }); } const dbClient: DbClient = new DbClient(); const incrementTimesServed: boolean = true; let incrementTimesResized: boolean = false; let incrementTimesDirectlyServed: boolean = false; const imageExistsPhysically: boolean = await FolderStructureUtils.imageWithResolutionExists(imageName, imageResolution); if (imageExistsPhysically) { incrementTimesDirectlyServed = true; dbClient.incrementValue(imageName, incrementTimesServed, incrementTimesResized, incrementTimesDirectlyServed); const imagePath = FolderStructureUtils.getImageWithResolutionPath(imageName, imageResolution); res.status(200).sendFile(imagePath); return; } const serverImageService: ServedImageService = new ServedImageService(); const originalServedImage = await serverImageService.getOriginalServedImage(imageName); // https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 if (originalServedImage.existsOnFileSystem) {// if image exists we can proceed to try and serve the resized image incrementTimesResized = true; dbClient.incrementValue(imageName, incrementTimesServed, incrementTimesResized, incrementTimesDirectlyServed); const resizedImage = await serverImageService.getResizedServedImage(originalServedImage, imageResolution); res.status(200).sendFile(resizedImage.absolutePath); } else { res.status(404).send({ error: `File ${imageName} does not exist on file system at ${originalServedImage.absolutePath}` }); } }); // define a route handler for the default home page app.get("/stats", async (req, res) => { const dbClient: DbClient = new DbClient(); const html = await dbClient.getImagesCollectionHtml(); res.status(200).send(html); }); // start the Express server app.listen(port, () => { // tslint:disable-next-line:no-console console.log(`server started at http://localhost:${port}`); });