Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moved imagemin out and provided a way to inject it if you want it #39

Merged
merged 2 commits into from
May 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ s3TestOptions.js
azureTestOptions.js
public/uploads
temp

package-lock.json
.jshintrc
81 changes: 77 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ uploadfs copies files to a web-accessible location and provides a consistent way
* Non-image files are also supported
* Web access to files can be disabled and reenabled
* Animated GIFs are preserved, with full support for scaling and cropping (if you have `imagemagick` or `imagecrunch`)
* On fire about file sizes? Images can be compressed even further with the `imagemin: true` option, if you are using `imagemagick` and you have [`jpegtrans`](http://sharadchhetri.com/2014/01/06/install-libjpegtran-using-yum-command-centos-6-x/), [`jpeg-recompress`](https://github.com/danielgtaylor/jpeg-archive#jpeg-recompress), [`mozjpeg`](https://nystudio107.com/blog/installing-mozjpeg-on-ubuntu-16-04-forge), [`optipng`](https://www.linuxhelp.com/install-jpegoptim-optipng-linux/) and `pngquant` available in your `PATH` (this is entirely optional). Note that `pngquant` is technically a lossy compression tool, your PNG's pixels will differ slightly from the original if you use `imagemin: true`.
* On fire about minimizing file sizes for your resized images? You can plug in `imagemin` and compatible tools using the `postprocessors` option.

You can also remove a file if needed.

Expand All @@ -28,11 +28,13 @@ You need:

* Patience, to wait for [Jimp](https://github.com/oliver-moran/jimp) to convert your images; or [Imagemagick](http://www.imagemagick.org/script/index.php), if you want much better speed and full animated GIF support; OR, on Macs, the very fast [imagecrunch](http://github.com/punkave/imagecrunch) utility. You can also write a backend for something else (look at `imagemagick.js`, `imagecrunch.js`, and `jimp.js` for examples; just supply an object with the same methods, you don't have to supply a factory function).

* If you want GIF support: [Imagemagick](http://www.imagemagick.org/script/index.php) or (Mac-specific) [imagecrunch](http://github.com/punkave/imagecrunch). `jimp` requires no installation of system packages, but it does not yet support GIF.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little confused if this is duplicating some of the information in the preceding paragraph?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I will give that text one more pass. What changed is that we realized there's no GIF support at all in the default jimp backend you get if you don't have imagemagick installed. Funny that no one complained...


* [gifsicle](https://www.lcdf.org/gifsicle/) is an optional tool that processes large animated GIFs much faster. Currently, Imagemagick is a prerequisite for using it. Turn it on with the `gifsicle: true` option when calling `init`. Of course you must install `gifsicle` to use it. (Hint: your operating system probably has a package for it. Don't compile things.)

* A local filesystem in which files stay put at least during the current request, to hold temporary files for Imagemagick's conversions. This is no problem with Heroku and most other cloud servers. It's just long-term storage that needs to be in S3 or Azure for some of them.

Note that Heroku includes Imagemagick. You can also install it with `apt-get install imagemagick` on Ubuntu servers. Homebrew can install `imagemagick` on Macs, or you can use [imagecrunch](http://github.com/punkave/imagecrunch), a fast, tiny utility that uses native MacOS APIs.
> Note that Heroku includes Imagemagick. You can also install it with `apt-get install imagemagick` on Ubuntu servers. Homebrew can install `imagemagick` on Macs, or you can use [imagecrunch](http://github.com/punkave/imagecrunch), a fast, tiny utility that uses native MacOS APIs.

## API Overview

Expand Down Expand Up @@ -70,7 +72,7 @@ For a complete, very simple and short working example in which a user uploads a

Here's the interesting bit. Note that we do not supply an extension for the final image file, because we want to var Imagemagick figure that out for us.

app.post('/', function(req, res) {
app.post('/', multipartMiddleware, function(req, res) {
uploadfs.copyImageIn(req.files.photo.path, '/profiles/me', function(e, info) {
if (e) {
res.send('An error occurred: ' + e);
Expand Down Expand Up @@ -331,6 +333,71 @@ It's up to you to create an Amazon S3 bucket and obtain your secret and key. See

S3 support is based on the official AWS SDK.

## Postprocessing images: extra compression, watermarking, etc.

It is possible to configure `uploadfs` to run a postprocessor such as `imagemin` on every custom-sized image that it generates. This is intended for file size optimization tools like `imagemin`.

Here is an example based on the `imagemin` documentation:

```
const imagemin = require('imagemin');
const imageminJpegtran = require('imagemin-jpegtran');
const imageminPngquant = require('imagemin-pngquant');

uploadfs.init({
storage: 'local',
image: 'imagemagick',
tempPath: __dirname + '/temp',
imageSizes: [
{
name: 'small',
width: 320,
height: 320
},
{
name: 'medium',
width: 640,
height: 640
}
],
postprocessors: [
{
postprocessor: imagemin,
extensions: [ 'gif', 'jpg', 'png' ],
options: {
plugins: [
imageminJpegtran(),
imageminPngquant({quality: '65-80'})
]
}
}
]
});
```

A file will not be passed to a postprocessor unless it is configured for the file's true extension as determined by the image backend (`gif`, `jpg`, `png` etc., never `GIF` or `JPEG`).

The above code will invoke `imagemin` like this:

```
imagemin([ '/temp/folder/file1-small.jpg', '/temp/folder/file2-medium.jpg', ... ], '/temp/folder', {
plugins: [
imageminJpegtran(),
imageminPngquant({quality: '65-80'})
]
}).then(function() {
// All finished
}).catch(function() {
// An error occurred
});
```

You may write and use other postprocessors, as long as they expect to be called the same way.

> Note that the second argument is always the folder that contains all of the files in the first argument's array. `uploadfs` expects your postprocessor to be able to update the files "in place." All of the files in the first argument will have the same extension.

If your postprocessor expects four arguments, uploadfs will pass a callback, rather than expecting a promise to be returned.

## About P'unk Avenue and Apostrophe

`uploadfs` was created at [P'unk Avenue](http://punkave.com) for use in many projects built with Apostrophe, an open-source content management system built on node.js. Appy isn't mandatory for Apostrophe and vice versa, but they play very well together. If you like `uploadfs` you should definitely [check out apostrophenow.org](http://apostrophenow.org). Also be sure to visit us on [github](http://github.com/punkave).
Expand All @@ -343,6 +410,12 @@ Feel free to open issues on [github](http://github.com/punkave/uploadfs).

## Changelog

### CHANGES IN 1.10.0

`imagemin` is no longer a dependency. Instead the new `postprocessors` option allows you to optionally pass it in. `imagemin` and its plugins have complicated dependencies that don't build smoothly on all systems, and it makes sense to leave the specifics of this step up to the users who want it.

Since setting the `imagemin: true` option doesn't hurt anything in 1.10.0 (you still get your images, just not squeezed quite as small), this is not a bc break.

### CHANGES IN 1.9.2

`mocha` and `lodash` upgraded to satisfy `npm audit`.
Expand Down Expand Up @@ -401,7 +474,7 @@ Every effort has been made to deliver 100% backwards compatibility with the docu

### CHANGES IN 1.4.0

* The new pure-JavaScript `jimp` image backend works "out of the box" even when ImageMagick is not installed. For faster operation and animated GIF support, you should still install ImageMagick. Thanks to Dave Ramirez for contributing this feature.
* The new pure-JavaScript `jimp` image backend works "out of the box" even when ImageMagick is not installed. For faster operation and GIF support, you should still install ImageMagick. Thanks to Dave Ramirez for contributing this feature.

### CHANGES IN 1.3.6

Expand Down
48 changes: 1 addition & 47 deletions lib/image/imagemagick.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ var im = require('gm').subClass({ imageMagick: true });
var childProcess = require('child_process');
var _ = require('lodash');
var async = require('async');
var imagemin = require('imagemin');

var optimizerOptions = {
'imagemin-pngquant': {
quality: '65-80'
}
};

module.exports = function() {
var options;
Expand All @@ -25,22 +18,6 @@ module.exports = function() {
*/
init: function(_options, callback) {
options = _options;
if (options.imagemin) {
self.optimizers = {};
self.optimizerNames = [ 'imagemin-jpeg-recompress', 'imagemin-mozjpeg', 'imagemin-pngquant', 'imagemin-optipng' ];
_.each(self.optimizerNames, function(name) {
try {
self.optimizers[name] = require(name);
} catch (e) {
// That's OK, this one is not available
console.warn('Unable to require ' + name + ', this optimizer may not have compiled properly in your environment, check that project\'s github issues');
}
});
// Only run those that exist
self.optimizerNames = _.filter(self.optimizerNames, function(name) {
return !!self.optimizers[name];
});
}
return callback(null);
},

Expand Down Expand Up @@ -396,30 +373,7 @@ module.exports = function() {

args.push('null:');

return spawnThen('convert', args, function (err) {
if (err) {
return callback(err);
}

if (!options.imagemin) {
return callback();
}
return imagemin(resizedPaths, context.tempFolder + '/', {
plugins: _.map(self.optimizerNames, function(name) {
return self.optimizers[name](optimizerOptions[name] || {});
})
})
.then(function(files) {
//= > [{data: <Buffer 89 50 4e …>, path: 'build/images/foo.jpg'}, …]
return callback(null);
})
.catch(function(err) {
console.warn('imagemin failed. One or more imagemin plugins may have a bug or require optional system packages not present on your system. The file will be somewhat larger due to lack of optimization. See the github issues of these projects: ' + self.optimizerNames.join(', '));
console.warn(err);
// returning ok callback as this additionnal compression doesn't prevent images usage
return callback(null);
});
});
return spawnThen('convert', args, callback);
}

function spawnThen(cmd, args, callback) {
Expand Down
Loading