88

I'm developing a web app on Node.js (+ express 4) where users can set their profile image by uploading it to the server. We already limit the file mimetype and max filesize, so the user can't upload more than 200KB png or jpeg images.

The problem is we'd like to resize (serverside) the uploaded image resolution to 200x200 to improve page loading and saving space on disk. After some research, all answers pointed to using any module based on ImageMagick or GraphicsMagick.

However, having to install ImageMagick/GraphicsMagick to do a simple image resizing seems too overkill for me, so, is there any other solution other than this for Node.js?

Edit: I've changed the accepted solution to sharp as the previous solution (lwip) is no longer maintained. Thanks for all your feedback!

Qchmqs
  • 1,732
  • 2
  • 19
  • 29
zacr0
  • 991
  • 1
  • 7
  • 9
  • Hi. I've a question. How to decrease the image size to below 200KB? Please, explain about the way. Thanks. – C.Petrescu May 09 '17 at 21:15
  • Hello, that question is worth posting it as a new one if you don't find any related one that had been previously posted. To give you some light, try looking for compression and resizing methods in the API provided for the tools you can find in this question. – zacr0 May 10 '17 at 15:43

12 Answers12

95

I would vote for sharp:

sharp('input.jpg')
  .resize(200, 200)
  .toFile('ouput.jpg', function(err) {
    // output.jpg is a 200 pixels wide and 200 pixels high image
    // containing a scaled and cropped version of input.jpg
  });

It's fast, typically 6x faster than the fastest imagemagick-based node bindings, and runs in very little memory, perhaps 10x less. sharp links to the libvips image library directly, there is no shelling out to an external program, and the library itself is faster and more efficient than *magick at this task. It supports useful things like stream, buffer and filesystem input and output, colour management, transparency, promises, overlays, WebP, SVG, and more.

As of sharp 0.20, npm will automatically download complete pre-compiled binaries on most platforms, so there's no need for node-gyp. Just enter:

npm install sharp

or:

yarn add sharp

And off you go.

Michał Zalewski
  • 2,432
  • 1
  • 20
  • 33
jcupitt
  • 7,772
  • 1
  • 20
  • 28
  • 7
    As of v0.12.0, `sharp` no longer has any external runtime dependencies for Linux and Windows users as it bundles a pre-compiled version of libvips. You'll find resizing operations are ~10x faster than LWIP and at a fraction of the memory usage. – Lovell Fuller Nov 23 '15 at 13:35
  • sharp/libvips doesn't support gif natively, so i ended up using lwip. – Palani Jun 27 '16 at 02:29
  • 4
    sharp added native gif and svg support with version 0.15 http://sharp.dimens.io/en/stable/changelog – jcupitt Jul 01 '16 at 08:46
  • I am considering sharp for Image manipulation, but I need it to resize the image and send the resized image in buffer over the network and not write to disk. How do I do that ? – Curious101 Aug 24 '16 at 17:05
  • nevermind, found the option .toBuffer(). It worked quite well. Thank you for a great module! – Curious101 Aug 24 '16 at 17:14
  • i feel retarded. I run `npm install sharp`, i get 100's of folder, and in sharp folder there are 100's of files. FFS i need a sharp.js file that i will put on my server with one mouse drag. – lxknvlk Oct 05 '16 at 15:03
  • 1
    It's 100s of files, but they are all managed for you automatically by npm, you don't need to think about it. – jcupitt Oct 05 '16 at 16:33
  • `sharp` as well `lwip` fail to install via npm, because they try to build something with gyp, which is pretty much doomed to fail in general under Windows. How can you install it with prebuilt binaries, that are said to be a bundled since v0.12? – CodeManX May 05 '17 at 18:01
  • 4
    @CoDEmanX Perhaps try running `npm install --global --production windows-build-tools` first. See also https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules – Lovell Fuller May 06 '17 at 09:46
  • @LovellFuller I installed the build tools in a PowerShell run as administrator, then installed sharp with success. Thanks! lwip compilation fails however: `fatal error C1189: #error: ZLIB_VERNUM != PNG_ZLIB_VERNUM` – CodeManX Feb 07 '18 at 19:25
  • sharp has just removed the need for node-gyp. If you have node 4, 6, 8 or 9 on 64-bit Linux or Windows, it will download complete binaries automatically. http://sharp.pixelplumbing.com/en/stable/install/ – jcupitt Mar 28 '18 at 08:23
  • when i try to install this on Linux im getting Leaving directory `/home/myhomeDir/node_modules/sharp/build' gyp ERR! build error gyp ERR! stack Error: `make` failed with exit code: 2. what is the reason for this? – CodeMind Apr 02 '18 at 07:45
  • Something odd about your platform I guess. Ask on the sharp issue tracker: https://github.com/lovell/sharp/issues they are very helpful. – jcupitt Apr 02 '18 at 09:15
  • 2019 update: qualty(100) for jpeg files is equivalent to 50, quality of the image will be lost – Johnny Derp Jan 24 '19 at 01:21
  • The sharp `quality()` setting uses the same scale as everyone else. All it does is pass the value down to your jpeg encoder. If you are seeing quality problems, the cause is probably elsewhere. – jcupitt Jan 24 '19 at 09:17
  • 1
    I built a little script makes it easy to *see* what things are doing and how it would look in content like boostrap cards and background:cover to best optimise the params, maybe its of intrest https://github.com/lcherone/sharp-test – Lawrence Cherone Feb 26 '19 at 19:07
  • For anyone using image buffer, the process is easy. return sharp(bytes) .resize(110, 80) .toBuffer() .then(resultBytes => { console.log(resultBytes); }) – Mahesh Jun 07 '19 at 20:19
71

I have recently started developing an image processing module for NodeJS without any runtime dependencies (read why). It's still at early stages, but already usable.

What you are asking for would be done as follows:

image.resize(200, 200, function(err, image){
    // encode resized image to jpeg and get a Buffer object
    image.toBuffer('jpg', function(err, buffer){
        // save buffer to disk / send over network / etc.
    });
});

More info at the module's Github repo.

EyalAr
  • 3,030
  • 1
  • 20
  • 27
  • 7
    Your module is awesome. However, it takes so much memory. I've tried to `writeFile` a `1.8Mb` image, and it requires 130Mb of memory. After that, I make a test by resizing a `4MB, 11000x6000` image to several thumbnails (640,560,480,...,160) and it takes approximately 1.7GB memory. Is that a bug? – Lewis Nov 05 '14 at 09:54
  • 2
    Hi @Orion, thanks for the feedback. Please head over to the Github repo and open an issue with some more details (OS, versions, code to reproduce this). We'll try to solve this together :) – EyalAr Nov 06 '14 at 01:59
  • @EyalAr Hey Eyal, how to resize and keep aspect? (resize to maximum possible size of width,height) without stretching – Daniel Krom Feb 11 '16 at 14:59
  • 10
    I would suggest not to use lwip in 2017. The package seems to be no longer supported and it has massive installation issues on Windows and now even on Unix platforms. – zerefel Jun 10 '17 at 00:36
  • 9
    lwip unfortunately is a dead project. [sharp](https://github.com/lovell/sharp) however still seems to be actively maintained. – laurent Oct 19 '17 at 20:41
16

Take a look at lwip : https://github.com/EyalAr/lwip

Very simple and easy to use

npm install lwip

and then in your node code,

// obtain an image object:
require('lwip').open('image.jpg', function(err, image){

  // check err...
  // define a batch of manipulations and save to disk as JPEG:
  image.batch()
    .scale(0.75)          // scale to 75%
    .rotate(45, 'white')  // rotate 45degs clockwise (white fill)
    .crop(200)            // crop a 200X200 square from center
    .blur(5)              // Gaussian blur with SD=5
    .writeFile('output.jpg', function(err){
      // check err...
      // done.
    });

});

I have successfully implemented this in my file uploader and it works like a charm.

Arvind
  • 671
  • 13
  • 46
  • 1
    This is what I was looking for, not having to install overkill, heavy external dependencies for a couple functions related to image resizing. – zacr0 Oct 09 '14 at 17:55
  • 3
    Btw @EyalAr is the author of this node module. His comment is also listed below. – Arvind Oct 10 '14 at 18:11
  • Very intuitive to install and work with. I really like that you are not required to implement any binary libraries like ImageMagick. – ChrisRich Mar 06 '15 at 09:06
  • That's exactly the same as this answer (lwip owner), but later: https://stackoverflow.com/a/24543924/1525495 – Jorge Fuentes González Apr 01 '19 at 11:04
13

There is a good image manipulation library written entirely in JavaScript, without dependencies to any other libraries, Jimp. https://github.com/oliver-moran/jimp

Example usage:

var Jimp = require("jimp");

// open a file called "lenna.png"
Jimp.read("lenna.png", function (err, lenna) {
    if (err) throw err;
    lenna.resize(256, 256)            // resize
         .quality(60)                 // set JPEG quality
         .write("lena-small.jpg"); // save
});
edtech
  • 1,508
  • 18
  • 20
  • How fast is this in comparison to ImageMagik? – Christopher Grigg Jul 15 '16 at 04:34
  • 2
    sharp has a set of benchmarks: http://sharp.dimens.io/en/stable/performance --- On that test, jimp is 5x slower than IM and 30x slower than sharp. Of course, fast enough is fast enough, and speed isn't the only factor to consider. – jcupitt Jul 22 '16 at 07:24
  • Realtime maybe not, but Jimp is awesome for just writing multiple-size thumbnail-files (and retrieve them as cached files afterwards). – coderofsalvation Mar 29 '17 at 17:42
  • jimp does not support webp and will not support in the nearest future. See: https://github.com/oliver-moran/jimp/issues/144 – Viacheslav Dobromyslov Aug 24 '20 at 22:27
9

sharp has enjoyed some popularity recently, but it’s the same idea as *Magick bindings.

However, having to install ImageMagick/GraphicsMagick to do a simple image resizing seems too overkill for me

Image resizing is anything but simple. The JPEG format is particularly complex, and there are several ways to scale graphics with results of varying quality, few of them easily implemented. Image processing libraries exist to do this job, so if there’s no other reason why you can’t install them, go for it.

Ry-
  • 199,309
  • 51
  • 404
  • 420
  • 13
    Maybe I'm a lazy developer, but as soon as I saw the installation process for ImageMagick and wondering how much I would spent on installing it on my Amazon AWS EC2 instance I immediately started looking for other options - especially considering that all I needed was the ability to resize images for thumbnails. – ChrisRich Mar 06 '15 at 09:09
7

According to images-manipulation-performance, Canvas is 2.3 times faster than ImageMagick.

Sample results:

Library Imges per Second Minimum Free Memory
sharp.js 9.501 929Mb
canvas.js 8.246 578Mb
gm.js 4.433 791Mb
gm-imagemagic.js 3.654 804Mb
lwip.js 1.203 54Mb
jimp.js 0.445 82Mb
Lee Goddard
  • 8,401
  • 3
  • 41
  • 52
Dimitry Ivanov
  • 173
  • 1
  • 3
4

If you don't need a large image, you can resize it on the client side before uploading it:

Reading files in JavaScript using the File APIs

Image resizing client-side with javascript before upload to the server

Many users might have a good picture of themselves from a smartphone, and many of them are over 200kB. Note that client-provided data is not to be trusted, so server-side checks still apply.

Community
  • 1
  • 1
Andrei Volgin
  • 38,656
  • 5
  • 43
  • 55
  • 2
    You can never trust the client, a user just needs to know the upload endpoint to send whatever he wants there. So, validations like the file size still apply. But, resizing on the client side is a good idea though. – Kev Mar 21 '15 at 00:50
1

I was using lwip (as previously suggested by arvind) but switched to png-crop. It seems to work a little faster for me (Win 8.1 x64, Node v0.12.7). The code in the repo looks incredibly lightweight, and operationally it's simple to use.

var pngcrop = require('png-crop');
var config = {left: 10, top: 100, height: 150, width: 150};
pngcrop.crop('cats.png','cats-cropped.png',config);

Of course, it'll only do png files...

Dan Caseley
  • 422
  • 5
  • 7
0

Sharp work very well and is easy to use with streams, work like a charm, but you need to compile it with the node version, this is a downside to it. I was using Sharp for image processing, with an image from an AWS S3 bucket and worked perfectly, but I had to use another module. GM didn't work for me, but Jimp worked very good!

You have to pay attention to the path of the written picture, it might give you some errors if you start the path with a "/".

This is how I used Jimp in nodeJS:

const imageUrl = `SOME_URL`;
let imgExported = 'EXPORTED_PIC.png';

Jimp.read(imageUrl)
    .then(image => {
        image   
            .resize(X, Y) 
            .write(`tmp/`+ imgExported, err => { 
                if(err) 
                    console.error('Write error: ', err);
                else { ... // don't forget to put a callback() } }

            });

Also watch out for the order of execution, put a callback so other things don't happen when you don't want to. Tried using "await" for the Jimp.read() but it didn't do the job well.

Alex Seceleanu
  • 139
  • 1
  • 5
  • From sharp 0.20, it'll automatically download a pre-compiled binary for your exact node version on most platforms, so there's no need to build anything. – jcupitt May 18 '19 at 12:46
  • Unfortunately it didn't work for me. I needed to use sharp in a read only file system, with various node.js versions and I had to download the sharp module for each node version I was using and it was taking too much time. – Alex Seceleanu May 27 '19 at 13:20
0

You can do this using jimp (node_module)

Local Write:

Jimp.read(path) // this can be url or local location
      .then(image=> {
          image
            .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
            .write('path-to-save');
      })
      .catch(err => {
        console.log(err);
      });

To upload to s3 or where ever you like.

Jimp.read(urls) // this can be url or local location
          .then(image=> {
              image
                .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
                .getBase64(Jimp.AUTO, (err, res) => {
                  const buf = new Buffer(
                    res.replace(/^data:image\/\w+;base64,/, ""),
                    "base64"
                  );
                  var data = {
                    Key: key,
                    Bucket: bucket,
                    Body: body,
                    ContentEncoding: "base64",
                    ContentType: "image/jpeg"
                  };
                  s3.putObject(data, function(err, data) {
                    if (err) {
                      throw err;
                    } else {
                      console.log("succesfully uploaded the image!");
                    }
                  });
                });
          })
          .catch(err => {
            console.log(err);
          });
Nouman Dilshad
  • 512
  • 7
  • 13
0

I like resize-img library for its simplicity.

const fs = require('fs');
const resizeImg = require('resize-img');

(async () => {
    const image = fs.readFileSync('unicorn.png');

    const newImage = await resizeImg(image, { width: 128, height: 128 });

    fs.writeFileSync('unicorn-128x128.png', newImage);
})();
ns16
  • 1,014
  • 2
  • 10
  • 19
0

Implemented image resize using Google Drive API v3. This method is recommended for Google Apps Script to insert images into Google Sheets.

Algorithm:

  1. Upload image to the Google Drive folder.
  2. Get image thumbnail public URL.
  3. Replace 'resize' parameter in the URL with necessary width and/or height. (Default thumbnail size is 220px).
  4. Download resized thumbnail from the Google Drive.

See example here: https://github.com/dobromyslov/google-drive-utils/blob/511c44c2c48862b47c60038423b7f71bf1d28f49/src/index.ts#L150

And beware of GDrive quotas:

  • queries per day: 1000000000
  • queries per 100 sec per user: 1000
  • queries per 100 sec: 10000
marc_s
  • 675,133
  • 158
  • 1,253
  • 1,388