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!
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.
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.
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.
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
});
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.
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
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.
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...
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.
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);
});
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);
})();
Implemented image resize using Google Drive API v3. This method is recommended for Google Apps Script to insert images into Google Sheets.
Algorithm:
Upload image to the Google Drive folder.
Get image thumbnail public URL.
Replace 'resize' parameter in the URL with necessary width and/or height. (Default thumbnail size is 220px).
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
This issue has been following me around for almost a year now, and I want to kill it, for my sake and for the sake of all.
I'm working on some banner ads that need to load in images from a client's site for display. When I tried to do this using AS2, I found out that AS2 doesn't let you do that. It's a bug in the language. There are workarounds for images on the local server, but images loaded from are not allowed to share their BitmapData, so those workarounds don't work. I ended up capitulating after about two months of banging my head against the desk and cursing Macromedia.
Now we are talking about moving to AS3 (finally) and I'm really excited. Or, I was really excited until I started doing some tests for image quality and found that there is very little change in image quality happening here. It's a repeat of my trials with AS2: everything loads perfectly in the IDE, I get all excited, I move the swfs over to the test server to run them online, and POOF - jaggies. Jaggies everywhere.
I've read a number of solutions online, none of which work. They include:
Setting target.content.smoothing to "true". Works great in the IDE. All improvements disappear in the browser.
Setting target.scaleX = target.scaleY to 1.01. It just breaks the swf.
Adding "new LoaderContext(true)" to my parameters for the load command. Does nothing.
Setting target.content.pixelSnapping to "always". Looks perfect in the IDE, not in the browser.
Setting a crossdomain.xml file. The images are showing up - they're being loaded, even if jaggedly, so there must be a functioning crossdomain file on the client's server, right?
So now I'm just stuck, and brokenhearted. Could anyone offer insight on my code, and why it might not be rendering as beautifully as it should be? Here is the client-safe version of the quick demo I am making (only the image URL has been deleted, everything else is as it is now):
import flash.events.Event;
function completeHandler(e:Event) {
e.target.content.pixelSnapping = "always";
e.target.content.smoothing = true;
}
var imgurl:String = "CLIENT'S IMAGE URL HERE";
var imageLoader01:Loader = new Loader();
var image01:URLRequest = new URLRequest(imgurl);
imageLoader01.load(image01);
imageLoader01.contentLoaderInfo.addEventListener(Event.COMPLETE,completeHandler);
addChild(imageLoader01);
imageLoader01.x = 2;
imageLoader01.y = 0;
imageLoader01.scaleX = imageLoader01.scaleY = .6;
var imageLoader02:Loader = new Loader();
var image02:URLRequest = new URLRequest(imgurl);
imageLoader02.load(image02);
imageLoader02.contentLoaderInfo.addEventListener(Event.COMPLETE,completeHandler);
addChild(imageLoader02);
imageLoader02.x = 100;
imageLoader02.y = 80;
imageLoader02.scaleX = imageLoader02.scaleY = .308;
var imageLoader03:Loader = new Loader();
var image03:URLRequest = new URLRequest(imgurl);
imageLoader03.load(image03);
imageLoader03.contentLoaderInfo.addEventListener(Event.COMPLETE,completeHandler);
addChild(imageLoader03);
imageLoader03.x = 200;
imageLoader03.y = 180;
imageLoader03.scaleX = imageLoader03.scaleY = .152;
var bannerLegend:legend = new legend();
addChild(bannerLegend);
Thank you very much in advance. Any help will be sorely appreciated.
Update: Here is the HTML embed code:
<div id="swf_mr_sc_wt_si"></div>
<script type="text/javascript">
<!--
var swfurl = "http://DOMAIN_WITHELD_SORRY/static/AS3.swf?m=DEFAULT&t=" + (new Date().getTime());
swfobject.embedSWF(swfurl, "swf_mr_sc_wt_si", 300, 250, "8.0.0", "");
// -->
</script>
<p>
Hope this helps.
Further Update: We are not listed in the crossdomain.xml file. But we can still load the jagged images. And those images, when loaded into the same swf run in the IDE, are smooth. I think I'm missing understanding of some kind of apocryphal knowledge here, because everything I read points to me being able to do this. This is VERY confusing.
It's because the image you're loading is located on another domain, and that domain's crossdomain.xml does not contain the domain the .swf is residing on, basically giving the .swf "permission" to access the image's pixel data (Yes, just enabling smoothing on an image loaded from another domain requires the same security as when reading the pixel data using BitmapData.draw(), which is a bit curious). When running in local security sandbox the restrictions are more lax, that's why it works running from the IDE.
Even if your domain were among the approved domains in the crossdomain.xml you might need to tell the Flash Player to check the policy file by sending in new LoaderContext(true) as a second argument to Loader.load() when loading the image.
Edit: I originally thought using loadBytes() would be a workaround, but it turns out it's not. I have removed that example code
I'm looking for a means to get the height and width of images from a given path locally. I know about imagemagick and graphicmagick but I'd prefer a method that doesn't involve installing extra software to the OS. If I can keep it to node modules that would be fantastic.
Does anyone have any ideas that may help me?
Worst case scenario, I'll use IM and GM but like it said would prefer to avoid this path.
You can use JIMP(JavaScript Image Manipulation Program). An image processing library for Node written entirely in JavaScript, with zero external or native dependencies. It has so many other image manipulation options available if you want.
var Jimp = require('jimp');
var image = new Jimp("./path/to/image.jpg", function (err, image) {
var w = image.bitmap.width; // width of the image
var h = image.bitmap.height; // height of the image
});
Hope this will help.
You can use a pure JS node module https://www.npmjs.org/package/image-size .. doesn't require installing anything extra
var sizeOf = require('image-size');
var dimensions = sizeOf('images/funny-cats.png');
console.log(dimensions.width, dimensions.height);
i have been using this code to store an image from imageviewer to device memory.
blobObj = imageView.toImage();
var f = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory,'img.png');
f.write(blobObj);
Titanium.Media.saveToPhotoGallery(f,{
success: function(e) {
Titanium.UI.createAlertDialog({
title:'Photo Gallery',
message:'Check your photo gallery for image '
}).show();
},
error: function(e) {
Titanium.UI.createAlertDialog({
title:'Error saving',
message:e.error
}).show();
}
});
What i want is to get the native path of currently saved image from memory
Thankyou
Dear Please see the below example to get native path where the image is stored in titanium
file system
// Native path
var filename = "image.png";
// Create the file in the application directory
bgImage = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory, filename);
// Write the image to the new file (image created from camera)
bgImage.write(image);
// bgImage.nativePath, it is alos native path, but you can get through below code.
nativePath = Titanium.Filesystem.applicationDataDirectory + Ti.Filesystem.separator + filename;
alert(nativePath);
Hopefully, it will for you. It is working on my side.
As this only works on iOS (see docs) i guess you are on iOS Platform.
If you create an image via blobObj = imageView.toImage(); you will receive a Titanium.Blob object. This object can be persisted somewhere in your app, it can also be temporarily available in memory.
If you store this image to the media gallery you won't get a file path to this image as this is not allowed by the iOS platform. So finally: What you want to do is not possible.
But: You can store the image yourself persistently within your app's data storage (using Ti.Filesystem).
To retrieve a file path for the temp object use f.nativePath or f.resolve().
Don't forget: You'll never get a path to the media gallery's photos. Even if you load an image from there you'll get a temporary path.
I am trying to convert some html to a png using wkhtmltoimage via nodejs and the node-wkhtml package.
I am able to a pdf without and problem. However, when I try to make a png instead of a pdf, the png is just band of colors instead of what it should be. This reported bug on the project site is very similar to what I am seeing (except I am trying to write a png and the bug is reported for a png).
It seems that the problem should only be present when using stdout on windows. Is there a work around? Is there any way to save the file without using stdout?
var fs = require('fs');
var wkhtml = require('node-wkhtml'),
createWriteStream = require('fs').createWriteStream;
var MyClass = module.exports = function()
{
var png = wkhtml.spawn('png');
png.stdout.pipe(createWriteStream('node_doc.png'));
png.stdin.end('<h1>Hello World</h1>');
}
new MyClass();
I ended up abandoning streams and node-wkhtml in general. I am now just writing a temp html file and calling wkhtmltoimage using spawn. Not ideal to have to make the temp file, but it is working.
child_process.spawn( 'wkhtmltoimage', [ 'temp.html', 'output.png' ] );