├── .gitignore ├── .jshintrc ├── README.md ├── app.js ├── lib └── handler.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | */**/.DS_Store -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "regexp": true 4 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | img-handler-server 2 | ================== 3 | 4 | A minimalist node.js image handler server to resize, crop, apply filters and more... 5 | 6 | Install 7 | ------- 8 | 9 | * First, install ImageMagick (brew install imagemagick / apt-get install imagemagick); 10 | * Clone this repository (git clone https://github.com/rodrigoalviani/img-handler-server.git); 11 | * Install modules (npm install); 12 | * Up your server (node app.js); 13 | * Done! 14 | 15 | 16 | Examples 17 | -------- 18 | 19 | Bounded: 20 | http://localhost:3000/http://38.media.tumblr.com/925556f88fa5603d13b728b73912511e/tumblr_n4avfmACuD1r7tv3oo1_1280.jpg?s=bounded&w=200 21 | 22 | Matted: 23 | http://localhost:3000/http://38.media.tumblr.com/925556f88fa5603d13b728b73912511e/tumblr_n4avfmACuD1r7tv3oo1_1280.jpg?s=matted&w=200&b=black 24 | 25 | Fill: 26 | http://localhost:3000/http://38.media.tumblr.com/925556f88fa5603d13b728b73912511e/tumblr_n4avfmACuD1r7tv3oo1_1280.jpg?s=fill&w=200&g=center 27 | 28 | Strict: 29 | http://localhost:3000/http://38.media.tumblr.com/925556f88fa5603d13b728b73912511e/tumblr_n4avfmACuD1r7tv3oo1_1280.jpg?s=strict&w=200 30 | 31 | 32 | Options (QueryStrings) 33 | ---------------------- 34 | 35 | querystring | name | description | default | options 36 | ----------- | ---- | ----------- | ------- | ------- 37 | s | strategy | resize strategy | bounded | bounded, matted, fill, strict 38 | w | width | width to resize in pixels | 100 | integer 39 | h | height | height to resize in pixels | 100 | integer 40 | q | quality | quality of generated image | null | 0 to 100 41 | g | gravity | where to cut a image - if applicable | Center | NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast 42 | b | background | background color (for matted strategy) | black | all colors 43 | f | filter | image color filter | null | grayscale, sepia, negate, lomo, gotham, hue 44 | fv | filter variable | variable to use with filter | null | on hue 0 to 360 45 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'), 4 | request = require('request'), 5 | handler = require('./lib/handler'), 6 | _ = require('underscore'), 7 | app = express(); 8 | 9 | /* 10 | Possible querystrings 11 | s: strategy [default: bounded] [bounded, matted, fill, strict] 12 | w: width [default: 100] 13 | h: height [default: 100] 14 | q: quality [default: 75] 15 | g: gravity [default: Center] [NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast] 16 | b: background [default: black] 17 | f: filter [default: null] [grayscale, sepia, negate, lomo, gotham, hue] 18 | f: filter variable [default: null] [on hue 0 to 360] 19 | */ 20 | 21 | app.get(/(.+)/, function (req, res) { 22 | var url = req.params[0].substring(1, req.params[0].length); 23 | 24 | if (url.substring(0, 4) !== 'http') { 25 | return false; 26 | } 27 | 28 | if (!req.query.w && req.query.h) { 29 | req.query.w = req.query.h; 30 | } 31 | 32 | if (req.query.w && !req.query.h) { 33 | req.query.h = req.query.w; 34 | } 35 | 36 | var options = { 37 | s: 'bounded', 38 | w: 100, 39 | h: 100, 40 | g: 'Center', 41 | b: 'black', 42 | q: 75 43 | }; 44 | 45 | _.extend(options, req.query); 46 | 47 | request({ url: url }) 48 | .on('response', function (img) { 49 | handler(img, options, function (err, str) { 50 | if (err) { 51 | console.log(err); 52 | } else { 53 | str.pipe(res); 54 | } 55 | }); 56 | }); 57 | }); 58 | 59 | app.listen(3000, function () { 60 | console.log('server listen port 3000'); 61 | }); 62 | -------------------------------------------------------------------------------- /lib/handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gm = require('gm').subClass({imageMagick: true}); 4 | 5 | function convert(item, options) { 6 | if (options.s.toLowerCase() === 'bounded') { 7 | item.strip().resize(options.w, options.h); 8 | } else if (options.s.toLowerCase() === 'matted') { 9 | item.strip().gravity('Center').resize(options.w, options.h).in('-background', options.b).extent(options.w, options.h).matte(); 10 | } else if (options.s.toLowerCase() === 'fill') { 11 | item.strip().gravity(options.g).resize(options.w, options.h + '^').extent(options.w, options.h); 12 | } else if (options.s.toLowerCase() === 'strict') { 13 | item.strip().resize(options.w, options.h + '!').extent(options.w, options.h); 14 | } 15 | 16 | if (options.f) { 17 | options.f = options.f.toLowerCase(); 18 | if (options.f === 'grayscale') { 19 | item.in('-normalize').in('-grayscale', 'Average'); 20 | } else if (options.f === 'sepia') { 21 | item.in('-normalize').in('-grayscale', 'Average').in('-fill', 'goldenrod').in('-tint', 100); 22 | } else if (options.f === 'negate') { 23 | item.in('-normalize').in('-negate'); 24 | } else if (options.f === 'lomo') { 25 | item.in('-normalize').in('-channel', 'R').in('-level', '22%').in('-channel', 'G').in('-level', '22%'); 26 | } else if (options.f === 'gotham') { 27 | item.in('-normalize').in('-modulate', '120,10,100').in('-fill', '#222b6d').in('-colorize', 20).in('-gamma', 0.5).in('-contrast'); 28 | } else if (options.f === 'hue' && options.fv !== undefined && options.fv >= 0 && options.fv <= 360) { 29 | item.in('-normalize').in('-modulate', '100,100,' + options.fv); 30 | } 31 | } 32 | 33 | if (options.q) { 34 | item.quality(options.q); 35 | } 36 | 37 | return item; 38 | } 39 | 40 | exports = module.exports = function (stream, options, cb) { 41 | var item = gm(stream); 42 | return cb('', convert(item, options).stream()); 43 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "img-handler-server", 3 | "version": "0.2.0", 4 | "description": "a image handler server", 5 | "author": "Rodrigo Alviani (https://github.com/rodrigoalviani)", 6 | "main": "app.js", 7 | "license": "MIT", 8 | "dependencies": { 9 | "request": "*", 10 | "express": "*", 11 | "gm": "*", 12 | "underscore": "*" 13 | } 14 | } --------------------------------------------------------------------------------