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
npm i express
you can read more in the npm site: express-npm
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:
"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/):
"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:
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:
/**
* 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:
/**
*
* @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:
/**
* 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
If you found this article useful, consider ❤️ hearting, 🦄 unicorning and 🔖 bookmarking it on DEV.to. It helps more than you know.
Top comments (2)
Great article!
Reccomended, thanks
Fille or File?