How to upload and customize images using sharp in Node.js
Tags: node, javascript, beginners, webdev
Published: 2020-08-21 15:31:15

![Alt Text](https://cdn.pixabay.com/photo/2017/10/09/15/38/upside-2833766_1280.jpg)
If you don't know "Sharp" - you should, It's an amazing package that lets you customize images before uploading it to the FTP. if you don't need to customize the image you can just use "fs" (but keep reading if you want to know more about this package).

So why I need to use customize image packages at all? For me, I didn't have a choice, because I got the wrong orientation of the image And needed to turn it to the regular side. This is one example of many, If you are interested to know how to implements "sharp" Keep reading.

### Step 1: installation of all the npm packages
`npm i sharp`
you can read more in the npm site: [sharp-npm](https://www.npmjs.com/package/sharp)

`npm i express`
you can read more in the npm site: [express-npm](https://www.npmjs.com/package/express)

### Step 2: Create index.js file (the main server file)
I must admit I Love OOP, I know javascript it's not OOP language, but  for me, it's more organized, this is my index.js:

```javascript
"use strict"

const Api = require('./express/routes/Api');
class Server {
  constructor() {
    this.express = require('express');
    this.app = this.express();
    this.path = require('path');
    this.apiRouters = this.express.Router();
    this.api = {};
    this.port = 0;
    this.bodyParser = require('body-parser');
  }
  /**
   * startExpressConfig - start Express Config
   */
  startExpressConfig() {
    this.app.use(this.bodyParser.urlencoded({
      extended: false,
      limit: '50mb'
    }));
    this.app.use(this.bodyParser.json({
      limit: '50mb'
    }));
    process.env.PORT ? this.port = process.env.PORT : this.port = 8000 //! process.env.PORT - production
  }
  /**
   * errorMiddleware - print error (in server and client regarding he api)
   */
  errorMiddleware() {
    this.app.use(function (err, req, res, next) {
      if (err.message === "Cannot read property 'catch' of undefined") { //! if user didn't found
        let errorMessage = `Got wrong with the request, please check the req.body`
        console.error(`client send incurrent request at : `, req.body)
        res.status(422).send({
          errorMessage
        })
      } else {
        console.error(`${err.message}`)
        res.status(422).send({
          error: err.message
        })
      }
    })
  }
  /**
   * activeApi - Open api routes
   */
  activeApi() {
    this.api = new Api(this.apiRouters,  this.path);
    this.app.use('/', this.apiRouters);
    // error middleware
    this.errorMiddleware()
    this.api.uploadImage();
    this.api.getImage();
  }
  /**
   * addAppLister - Active server port
   */
  addAppLister() {
    this.app.listen(this.port, () => {
      console.log(`Running on port ${this.port}`)
    })
  }
  /**
   * activateServer - Active all index methods
   */
  activateServer() {
    this.startExpressConfig();
    this.activeApi();
    this.addAppLister();
  }
}
const server = new Server();
server.activateServer();
```
### Step 2: Creating The API module
Create Api.js file (at my hierarchy folder it's under /express/routes/):

```javascript
"use strict"

const ImageUpload = require('../../image_module/ImageController');

class Api {
    constructor(router, path) {
        this.router = router;
        this.path = path;
        this.imageUpload = new ImageUpload(path);
    }
    /**
     * getImage - get Image from server 
     * ! Input - imageName - the name og the image, request looks like http://localhost:8000/images?imageName=image_1586948956767.jpg
     * ! Output - the image
     * TODO : make valid get image request
     */
    getImage() {
        this.router.get('/images', (req, res, next) => {
            let name = req.query.imageName
            const path = this.path.join(__dirname, '../../images/')
            res.status(200).sendFile(`${path}${name}`);
        })
    }
    /**
     * uploadImage - upload image to server
     * ! Input -  request body looks like {"base64":"/9j/....","height":960,"width":1280,"pictureOrientation":1,"deviceOrientation":1}
     * ! Output - the image name
     */
    uploadImage() {
        this.router.post('/upload/image', async (req, res, next) => {
            const imageName = `image_${Date.now()}.jpg`
            let answer = await this.imageUpload.addImage(req, imageName)
            if (answer === "O.K") {
                await res.status(200).send(imageName);
            } else {
                console.error(`${answer}`)
                await res.status(422).send({
                    error: answer
                })
            }
            gc();
            return;
        })
    }
}
module.exports = Api
```
Let's focus on the `uploadImage` method:
* The `imageName` must be a unique name.
* The `addImage` return if the image was uploaded (you will see on the next steps)

##Step 3: Build the Image upload module
First, let's set the basic class variables by creating the constructor:
```javascript
    constructor(path) {
        this.path = path;
        this.sharp = require("sharp")
        this.authoriseFilles = {
            R0lGODdh: "image/gif",
            R0lGODlh: "image/gif",
            iVBORw0KGgo: "image/png",
            "/9j/": "image/jpg",
        }
    }
```
* The `authoriseFilles ` it's the list of files that authorized to upload to the server FTP.

Now, we need to create the method that checks if the file is valid, like this:
```javascript
    /**
     * detectMimeType - check if the file is authorized (only images)
     * @param {sting} base64 - base64 string encoding
     */
    detectMimeType(base64) {
        let answer = ""
        for (let string in this.authoriseFilles) {
            if (base64.indexOf(string) === 0) {
                answer = "O.K";
            }
        }!answer ? answer = "not vaild fille" : null;
        return answer;
    }
```
Let's create the `readFille` method:
```javascript
    /**
     * 
     * @param {string} path - image path and name
     * @param {Buffer} fille - Buffer fille to upload to server
     * @param {number} imageOrientation - image Orientation : check if the orientation is correct
     */
    async readFile(path, fille, imageOrientation) {
        gc();
        this.sharp.cache(false)
        let data = await this.sharp(fille).metadata()
        if (data.orientation !== imageOrientation) {
            await this.sharp(fille).rotate(360).resize(data.width).toFile(path);
        } else {
            await this.sharp(fille).toFile(path);
        }
        gc();
        return
    }
```
* The `this.sharp.cache(false)` for disabling the sharp cache (it helps with problems regarding memory leak)
* `await this.sharp(fille).metadata()` get object data on the file - with it we can check the orientation
* If the orientation isn't correct, sharp will rotate it to the right orientation and upload it to the path (`toFile(path)`)

Now, let's build the controller method:
```javascript
    /**
     * addImage - main function of this module
     * @param {object} req - the requrest object
     * @param {sting} imageNmae - the image name
     */
    async addImage(req, imageNmae) {
        let answer = await this.detectMimeType(req.body.base64);
        if (answer === "O.K") {
            const imgdata = JSON.stringify(req.body.base64);
            const buf = Buffer.from(imgdata, 'base64');
            const path = this.path.join(__dirname, '../images/') + imageNmae;
            this.readFile(path, buf, req.body.pictureOrientation)
        }
        return answer;
    }
```
## Conclusion
The "rotate image" it's only one example of what `sharp` can do. There is a lot of other packages for customizing images, I tried to use `jimp` but it caused me a memory leak (maybe I didn't use it right, I'm not sure). The bottom line it's important for you to know about this option.

If you want to see the all code you can see it (and clone it if you want) in this GitHub repository: [image-uploader](https://github.com/tomeraitz/image-uploader)

If you found this article useful, consider ❤️ hearting, 🦄 unicorning and 🔖 bookmarking it on DEV.to. It helps more than you know.