Skip to content
This repository has been archived by the owner on May 17, 2019. It is now read-only.

Commit

Permalink
Merge pull request #22 from dariuszor/feature/compress_image
Browse files Browse the repository at this point in the history
Feature/compress image
  • Loading branch information
Panshul Gupta committed Apr 21, 2016
2 parents 7ca38a8 + f670311 commit 2f227a4
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Options:
- `descriptions <path>` - Required
- `regex [name]` - Optional
- `logFile <path>` - Optional
- `compress` - Optional
- `sendMetrics` - Optional
- `metricsPrefix` - Optional

Expand Down
Binary file added examples/darthMeow.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions examples/git-cat.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/productImage.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/saberCat.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"commander": "2.3.0",
"debug": "2.0.0",
"easyimage": "1.0.2",
"imagemin": "4.0.0",
"knox": "0.9.1",
"lynx": "0.2.0",
"pkginfo": "0.3.0",
Expand Down
3 changes: 2 additions & 1 deletion src/coffee/commands/images-convert.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ program
.option '-d, --descriptions <path>', 'set image descriptions file path'
.option '-r, --regex [name]', 'an optional RegExp used for filtering listed files (e.g.: /(.*)\.jpg/)', ''
.option '-l, --logFile <path>', 'optionally log to a file instead of printing to console (errors will still be printed to stderr)'
.option '-C, --compress', 'optionally compress images', false
.option '--sendMetrics', 'optionally send statsd metrics', false
.option '--metricsPrefix <name>', 'optionally specify a prefix for the metrics'
.parse process.argv
Expand Down Expand Up @@ -55,7 +56,7 @@ try
s3client.on 'progress', -> bar.tick()

# process files
s3client.resizeAndUploadImages files, description, tmpDir
s3client.resizeCompressAndUploadImages files, description, tmpDir, program.compress
.then ->
Logger.info 'Finished processing / converting %s images for prefix %s', totFiles, headers.prefix
Promise.resolve()
Expand Down
36 changes: 36 additions & 0 deletions src/coffee/compress.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
debug = require('debug')('s3utils-compress')
Promise = require 'bluebird'
fs = Promise.promisifyAll require('fs')
imagemin = require('imagemin')
_ = require 'underscore'

###*
* Compress images with basic configuration from Imagemin librabry
###
class Compress

@compressImage: (source, destination, extension) ->
new Promise (resolve, reject) ->
debug 'About to compress image from path: %s', source
switch extension
when "png" then plugin = imagemin.optipng({optimizationLevel: 3})
when "gif" then plugin = imagemin.gifsicle({interlaced: true})
when "svg" then plugin = imagemin.svgo()
else plugin = imagemin.jpegtran({progressive: true})

fs.stat source, (err, stat) ->
if err == null
(new imagemin)
.src(source)
.dest(destination)
.use(plugin)
.run (err) ->
if err
reject 'Error while compressing file: ' + err
else
resolve true
else
debug 'Error while getting stats for source: %s, with error %s', source, err
reject 'Error while getting stats for source: ' + source

module.exports = Compress
20 changes: 13 additions & 7 deletions src/coffee/services/s3client.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ knox = require 'knox'
Lynx = require 'lynx'
Promise = require 'bluebird'
easyimage = require 'easyimage'
Compress = require '../compress'
{CustomError} = require '../errors'
fs = Promise.promisifyAll require('fs')

Expand Down Expand Up @@ -190,14 +191,15 @@ class S3Client
###*
* @private
*
* Resizes and uploads a given image to the bucket
* Resizes, optionally compress and uploads a given image to the bucket
* @param {String} image The path the the image
* @param {String} prefix A prefix for the image key
* @param {Array} formats A list of formats for image resizing
* @param {String} [tmpDir] A path to a tmp folder
* @param {Boolean} compress Information if compressing is required
* @return {Promise} A promise, fulfilled with the upload response or rejected with an error
###
_resizeAndUploadImage: (image, prefix, formats, tmpDir = '/tmp') ->
_resizeCompressAndUploadImage: (image, prefix, formats, tmpDir = '/tmp', compress) ->

extension = path.extname image
basename = path.basename image, extension
Expand All @@ -213,7 +215,10 @@ class S3Client
dst: tmp_resized
width: format.width
height: format.height
.then (image) =>
.then ->
if compress
Compress.compressImage(tmp_resized, tmpDir, extension)
.then =>
@sendMetrics 'increment', 'image.resized'
header = 'x-amz-acl': 'public-read'
aws_content_key = @_imageKey "#{prefix}#{basename}", format.suffix, extension
Expand All @@ -226,14 +231,14 @@ class S3Client
, {concurrency: 2}

###*
* Resizes and uploads a list of images to the bucket
* Internally calls {@link _resizeAndUploadImage}
* Resizes, optionally compress and uploads a list of images to the bucket
* Internally calls {@link _resizeCompressAndUploadImage}
* @param {Array} images A list of images to be processed
* @param {Object} description A config JSON object describing the images conversion
* @param {String} [tmpDir] A path to a tmp folder
* @return {Promise} A promise, fulfilled with a successful response or rejected with an error
###
resizeAndUploadImages: (images, description, tmpDir = '/tmp') ->
resizeCompressAndUploadImages: (images, description, tmpDir = '/tmp', compress) ->

Promise.map images, (image) =>
name = path.basename(image.Key)
Expand All @@ -247,7 +252,8 @@ class S3Client
response.pipe stream
response.on 'end', resolve
response.on 'error', reject
.then => @_resizeAndUploadImage image.Key, description.prefix, description.formats, tmpDir
.then => @_resizeCompressAndUploadImage image.Key, description.prefix, description.formats,
tmpDir, compress
.then (result) =>
source = "#{description.prefix_unprocessed}/#{name}"
target = "#{description.prefix_processed}/#{name}"
Expand Down
39 changes: 39 additions & 0 deletions src/spec/compress.spec.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
_ = require 'underscore'
Promise = require 'bluebird'
Compress = require '../lib/compress'
fs = Promise.promisifyAll require('fs')

examplesFolder = "" + __dirname + "/../examples/"
compressedFolder = examplesFolder + "compressed/"
describe 'Compress', ->

beforeEach ->
try
fs.mkdirSync compressedFolder
catch e
if e.code != 'EEXIST'
throw e

afterEach ->
fs.rmdirSync compressedFolder

_.each [
"darthMeow.jpg"
"saberCat.gif"
"stormtroopocat.png"
"git-cat.svg"
"productImage.jpg"
], (fileName) ->
it "should compress file (#{fileName})", (done) ->
extension = fileName.substr(fileName.length - 3)
sourcePath = examplesFolder + fileName
destinationPath = compressedFolder + fileName
ext = Compress.compressImage sourcePath, compressedFolder, extension
.then (result) ->
expect(result).toBe(true)
fileSizeInBytesBeforeCompressing = fs.statSync(sourcePath)["size"]
fileSizeInBytesAfterCompressing = fs.statSync(destinationPath)["size"]
expect(fileSizeInBytesBeforeCompressing).not.toBeLessThan(fileSizeInBytesAfterCompressing)
fs.unlinkAsync destinationPath
.then -> done()
.catch (error) -> done(error)
49 changes: 47 additions & 2 deletions src/spec/services/s3client.spec.coffee
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
_ = require 'underscore'
Promise = require 'bluebird'
S3Client = require '../../lib/services/s3client'
Compress = require '../../lib/compress'
fs = Promise.promisifyAll require('fs')
easyimage = require 'easyimage'

CREDENTIALS =
key: '1111111'
Expand Down Expand Up @@ -90,8 +92,8 @@ describe 'S3Client', ->
spyOn(@s3client, 'putFile').andCallFake -> new Promise (resolve, reject) -> resolve()
@s3client.putDir "#{__dirname}/../../examples", 'dest/examples', {}
.then =>
expect(@s3client.putFile.calls[0].args).toEqual ["#{__dirname}/../../examples/descriptions.json", 'dest/examples/descriptions.json', {}]
expect(@s3client.putFile.calls[1].args).toEqual ["#{__dirname}/../../examples/stormtroopocat.png", 'dest/examples/stormtroopocat.png', {}]
expect(@s3client.putFile.calls[0].args).toEqual ["#{__dirname}/../../examples/darthMeow.jpg", 'dest/examples/darthMeow.jpg', {}]
expect(@s3client.putFile.calls[1].args).toEqual ["#{__dirname}/../../examples/descriptions.json", 'dest/examples/descriptions.json', {}]
done()
.catch (err) -> done(err)

Expand All @@ -101,3 +103,46 @@ describe 'S3Client', ->
spyOn(@s3client._knoxClient, 'copyFileAsync')
@s3client.copyFile 'foo', 'bar'
expect(@s3client._knoxClient.copyFileAsync).toHaveBeenCalledWith 'foo', 'bar'

describe ':: resizeCompressAndUploadImages', ->

path = "" + __dirname + "/../../examples"
source = path + "/productImage.jpg"
destination = path + "/productImage2.jpg"
filesList = [{Size: 1, Key: 'foo'}]
description =
"prefix": "products/"
"formats": [
"suffix": "_thumbnail"
"width": 240
"height": 240
]

beforeEach ->
easyimagePromise = easyimage.resize
src: source
dst: destination
width: 200
spyOn(easyimage, 'resize').andCallFake -> easyimagePromise
spyOn(Compress, 'compressImage')
spyOn(@s3client, 'putFile').andCallFake -> new Promise (resolve, reject) -> resolve()

afterEach ->
fs.unlinkAsync path + "/foo"
fs.unlinkAsync destination

it 'should call compress function', (done) ->

@s3client.resizeCompressAndUploadImages filesList, description, path, true
.then ->
expect(Compress.compressImage).toHaveBeenCalled()
.then -> done()
.catch (err) -> done(err)

it 'shouldn\'t call compress function', (done) ->

@s3client.resizeCompressAndUploadImages filesList, description, path, false
.then ->
expect(Compress.compressImage).not.toHaveBeenCalled()
.then -> done()
.catch (err) -> done(err)

0 comments on commit 2f227a4

Please sign in to comment.