🎉 I'm releasing 12 products in 12 months! If you love product, checkout my new blog workingoutloud.dev

Back to home

Depth Sharp Cli

    Intro to Sharp

    Sharp is a powerful image compression library for Node. As defined by it's summary on Github, Sharp is a "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. Uses the libvips library."

    It's simple API means that usage is incredibly simple. It would simply even just reduce image size with the following:

    // example.js // usage from cli: node example.js const sharp = require("sharp"); (async () => { await sharp("path/to/file.jpg").toFile("path/to/file-output.jpg"); })();

    Project

    While Sharp has some incredible features I recommend delving in, in this scenario we will create a simple script that runs like a command line tool.

    This project will be taking some liberties and assuming some knowledge of the fs, util and yargs-parser modules.

    1. Installation

    Initialise a new npm project and run the following:

    # using npm npm install yargs-parser sharp # OR using yarn yarn add yargs-parser sharp

    2. Basic setup

    In the below code, we will create a basic setup to use with node on the CLI.

    We will do the following:

    1. Require modules.
    2. Set up a basic help fallback to display when no args equate to a command.
    3. Add a basic compressImage function that fires on arg "compress".
    4. Add a basic switch function "run" to run when the node script is executed.
    5. Run that switch function.

    Create a file index.js at root of your project and add the following:

    // 1. Require modules. const cwd = process.cwd(); const yp = require("yargs-parser"); const argv = yp(process.argv.slice(2)); const sharp = require("sharp"); const fs = require("fs"); const util = require("util"); // 2. Set up a basic help fallback to display when no args equate to a command. const help = ` Usage $ sharp <action> <option> Action --- compress path/to/file Compress image --width=800, --height=null batch path/to/folder Compress folder of images --width=800, --height=null Examples $ sharp compress path/to/file $ sharp compress path/to/file --width 200 `; // 3. Add a basic compressImage function that fires on arg "compress". /** * Compress an image. * * @param {*} file The path to the file. */ const compressImage = async () => { try { console.log("[node index.js compress] fires!"); } catch (err) { console.error("Errored", err); } }; // 4. Add a basic switch to run when the node script is executed. /** * Run a function based on the argument provided. * */ const run = async () => { console.log("Running Image Compression CLI"); try { switch (argv._[0]) { case "compress": compressImage(); break; default: console.log(help); break; } } catch (err) { console.error(err); } }; // 5. Run that switch function. run();

    From the command line, if you now run node index.js compress you should get the console.log message that the compressImage function fired and for any argument the help variable should log out!

    3. Adding basic compression to a file

    Now in the compressImage function, we will update the code to take a file argument that is passed as an argument from the command line.

    To pass this argument, we will add argv._[1] as the argument passed in the switch that calls the compressImage function.

    Sharp itself requires an output file name that is different to the input, so for the sake of the example, we will just add some logic to set a file file-name.jpg to output as file-name-compressed.jpg (this assumption goes off the idea that there is only one '.' within the file name). This naming can be done with some small string manipulation and will be set as output.

    We will also update the code so that if our command line passes a width flag, it will resize the image to that width. We can do this with a basic if/else statement block to determine whether or not to fire the resize method or not.

    Our updated code now looks like so:

    // Previous code omitted for brevity /** * Compress an image. * * @param {*} file The path to the file. */ const compressImage = async (file) => { try { const outputArr = file.split("."); const output = outputArr[0] + "-compressed." + outputArr[1]; let res; if (argv.width) { res = await sharp(file).resize(argv.width).toFile(output); } else { res = await sharp(file).toFile(output); } const result = util.inspect(res, { depth: null }); console.log(`Image compressed! ${result}`); } catch (err) { console.error("Failed to upload", err); } }; /** * Run a function based on the argument provided. * */ const run = async () => { console.log("Running Image Compression CLI"); try { switch (argv._[0]) { case "compress": compressImage(argv._[1]); break; default: console.log(help); break; } } catch (err) { console.error(err); } }; run();

    Place an image named example.jpg (or whatever) in an image folder now and run node index.js compress image/example.jpg. You will now see we have a image/example-compressed.jpg file!

    Running that same code again setting the width flag will also resize an image ie node index.js compress image/example.jpg --width=200. Incredible!

    4. Batch compress all images in a file

    This time we will make use of a basic Node fs function readdirSync to batch process a bunch of images.

    The code below takes a folder as an argument, reads all the files within that folder and runs the compressImage function on each one!

    The bottom of our updated code now looks like so:

    // Prev code omitted for brevity /** * Compress multiple images * * @param {*} folder The folder path to batch compress */ const batchCompress = async (folder) => { try { fs.readdirSync(folder).forEach((file) => { const filepath = cwd + "/" + folder + "/" + file; compressImage(filepath); }); } catch (err) { console.error(err); } }; /** * Run a function based on the argument provided. * */ const run = async () => { console.log("Running Image Compression CLI"); try { switch (argv._[0]) { case "compress": compressImage(argv._[1]); break; case "batch": batchCompress(argv._[1]); break; default: console.log(help); break; } } catch (err) { console.error(err); } }; run();

    If we now place multiple files in our image folder, we could run node index.js batch image to batch process all the images! In fact, we can even run node index.js batch image --width=200 to limit the width of all those images processed!

    5. Final code

    Our final code now looks like the following:

    const cwd = process.cwd(); const yp = require("yargs-parser"); const argv = yp(process.argv.slice(2)); const sharp = require("sharp"); const fs = require("fs"); const util = require("util"); const help = ` Usage $ sharp <action> <option> Action --- compress path/to/file Compress image --width=800, --height=null batch path/to/folder Compress folder of images --width=800, --height=null Examples $ sharp compress path/to/file $ sharp compress path/to/file --width 200 `; /** * Compress an image. * * @param {*} file The path to the file. */ const compressImage = async (file) => { try { const outputArr = file.split("."); const output = outputArr[0] + "-compressed." + outputArr[1]; let res; if (argv.width) { res = await sharp(file).resize(argv.width).toFile(output); } else { res = await sharp(file).toFile(output); } const result = util.inspect(res, { depth: null }); console.log(`Image compressed! ${result}`); } catch (err) { console.error("Failed to upload", err); } }; /** * Compress multiple images * * @param {*} folder The folder path to batch compress */ const batchCompress = async (folder) => { try { fs.readdirSync(folder).forEach((file) => { const filepath = cwd + "/" + folder + "/" + file; compressImage(filepath); }); } catch (err) { console.error(err); } }; /** * Run a function based on the argument provided. * */ const run = async () => { console.log("Running Image Compression CLI"); try { switch (argv._[0]) { case "compress": compressImage(argv._[1]); break; case "batch": batchCompress(argv._[1]); break; default: console.log(help); break; } } catch (err) { console.error(err); } }; run();

    Next steps

    This is a basic intro in creating a small, useful image compression CLI tool. Sharp is incredibly powerful, so add more to this as you see fit! This doubles as an example of creating a very basic CLI tool, so you could even get rid of the sharp code and add whatever you see fit!

    Depth is a series that goes into more detail for projects than it's friendly counterpart series "Hello".

    Personal image

    Dennis O'Keeffe

    @dennisokeeffe92
    • Melbourne, Australia

    Hi, I am a professional Software Engineer. Formerly of Culture Amp, UsabilityHub, Present Company and NightGuru.
    I am currently working on Visibuild.

    1,200+ PEOPLE ALREADY JOINED ❤️️

    Get fresh posts + news direct to your inbox.

    No spam. We only send you relevant content.

    Depth Sharp Cli

    Introduction

    Share this post