Writing an image compression app with NodeJS, imagemin & pngquant (Part III)
Part III into writing an image compression app with NodeJS, imagemin & pngquant.
Hey guys, welcome back to Part III of my mini-app-cli-ish image compression series! If you haven't read part Part II, you can do so here. By the way, ghost added image compression to their core so your images will be compressed automatically by default!
Anyway, I'll continue this tutorial series because I'm starting a new Fotohaecker Project from scratch with Elixir & Phoenix Framework and I need a compression CLI to process users photo uploads.
Updating our dependencies
Because my last post is a bit old, we first want to check for dependency updates. For this, I find two libraries very useful: npm-check-updates and npm-check. The first one is more popular but the second has a graphical User-Interface - so you decide!
npm-check-updates: ncu
npm-check: npm-check
(note, I added the -u
flag to skip unused dependencies warnings, as we haven't actually used mozjpeg etc., yet.)
Into compression
Compression model
Now, lets get started with the actual compression of our images. It's triggered by the CLI command node run.js foo.jpg --compress [compression-level]
and evaluated in our code src/cli-controller.js:18~25
.
We can start adding the imagemin
calls right in the controller but I'd much rather prefer a model for this. First, let's download a sample image to compress and place it into /images/test.jpg
. Also, add a ./src/compression-model.js
:
Note the following things:
- with
fs.promises.access
, we check if thesourcePath
-File exists. Technically, we're introducing race-conditions with that but that's fine for me. - We're passing
options
to ourcompressImage
function but only usesourcePath
&destinationPath
for now. imagemin
is asynchronous so ourcompressImage
function needs to beasync
aswell to callawait imagemin
. We'll come to that later.- Finally, we're printing out the result of imagemin with
console.debug()
.
updating our CLI controller
Then, we want to expand our CLI with a parameter that determines the destination to save the compressed image as. Our CLI will look like this then: node run.js [sourceFile] [desinationFile] [--compress] [compression-level]
. With this, we'll also refactor our code a bit so missing parameters are catched earlier:
Note the following things:
- As
imagemin
is asynchronous (That means that we have to wait for imagemin to finish processing our image before we can actually say that it succeeded and furthermore process it), we also needed to change all functions upstream to beasync
. You can find a nice article about asynchronous concepts on MDN - I added a
checkArgsForErrors
function that adds up errors that might occur. When we start resizing, we can also move the check if the source file exists here - With
const [sourcePath, destinationPath, command, ...options] = args;
,args
are destructured into more meaningful variables that we can use in the switch.
Make sure to also update the controller function call in run.js:9~12
:
Run it!
You can now run our CLI with node run.js ./images/test.jpg ./images/compressed/test.jpg --compress
If the source file can't be found, our CLI will print out an error just like this:
Wrapping it up
Today, we refactored our cli-controller
and added an asynchronous compression-model
that takes different options to pass it to imagemin
.
If you want to clone the project, you can: https://github.com/fschoenfeldt/ghost-compression
Thanks for checking out my guide series for writing an image compression app with NodeJS. If you have any questions, feel free to contact me via https://fschoenfeldt.de - Part IV is coming, very soon! x
Photo by Tim Mossholder on Unsplash