Server image resizing and cropping on the fly base on ImageMagick+MozJPEG
Image resizing, cropping and compression on the fly with the impressive MozJPEG compression algorithm. One Docker container to build your own Cloudinary-like service.
Fetch an image from anywhere; resize, compress, cache and serve... and serve, and serve, and serve...
You pass the image URL and a set of keys with options, like size or compression. Flyimg will fetch the image, convert it, store it, cache it and serve it. The next time the request comes, it will serve the cached version.
<!-- https://www.mozilla.org/media/img/firefox/firefox-256.e2c1fc556816.jpg --> <img src="https://www.myservice.io/upload/w_333,h_333,q_90/https://www.mozilla.org/media/img/firefox/firefox-256.e2c1fc556816.jpg">
- Width: 300
- Height: 250
- Crop if necesary:
This will serve the image.
Change the first part of the path from
path, like so:
https://oi.flyimg.io/path/w_300,h_250,c_1/http://medula.cl/t/resize-test_1920.jpg will output in the body of the response:
- Width: 300
- Height: 250
- Note that we ommit the crop parameter
- Width: 200
- Height: 200
- Rotate: 90
- Width: 200
- Height: 200
- Quality: 30
- Installation [Deployment mode]
- Installation [Development Mode]
- Testing Flyimg service
- How to transform images
- Basic Option details
- Application Server Options
- Security: Restricting Source Domains
- Security: Signature Generation
- Run Unit Tests
- How to Provision the application on
- Technology stack
- Enable Xdebug
- Demo Application running
You will need to have Docker on your machine. Optionally you can use Docker machine to create a virtual environment. We have tested on Mac, Windows and Ubuntu.
Pull the docker image
docker pull flyimg/flyimg-build
Start the container
docker run -itd -p 8080:80 flyimg/flyimg-build
You can spin up your own working server in 10 minutes using the provision scripts for AWS Elastic Beanstalk or the DigitalOcean Ubuntu Droplets (more environments to come). For other environments or if you want to tweak and play in your machine before rolling out, read along...
You can use
composer for the first step.
git clone https://github.com/flyimg/flyimg.git
Create the project with
composer create .
composer create-project flyimg/flyimg
CD into the folder and to build the docker image by running:
docker build -t flyimg .
This will download and build the main image, It will take a few minutes. If you get some sort of error related to files not found by apt-get or similar, try this same command again.
Then run the container:
docker run -itd -p 8080:80 -v $(pwd):/var/www/html --name flyimg flyimg
For Fish shell users:
docker run -itd -p 8080:80 -v $PWD:/var/www/html --name flyimg flyimg
The above command will make the Dockerfile run supervisord command which launches 2 processes: nginx and php-fpm and starts listening on port 8080.
IMPORTANT! If you cloned the project, only for the first time, you need to run
composer install inside the container:
docker exec -it flyimg composer install
Again, it will take a few minutes to download the dependencies. Same as before, if you get some errors you should try running
composer install again.
You can navigate to your machine's IP in port 8080 (ex: http://127.0.0.1:8080/ ) ; you should get a message saying: Hello from Flyimg! and a small homepage of Flyimg already working. If you get any errors at this stage it's most likely that composer has not finished installing or skipped something.
You can test your image resizing service by navigating to: http://127.0.0.1:8080/upload/w_130,h_113,q_90/https://www.mozilla.org/media/img/firefox/firefox-256.e2c1fc556816.jpg
This is fetching an image from Mozilla, resizing it, saving it and serving it.
You go to your server URL
http://imgs.kitty.com and append
/upload/; after that you can pass these options below, followed by an underscore and a value
w_250,q_50 Options are separated by coma (configurable to other separator).
After the options put the source of your image, it can be relative to your server or absolute:
So to get a pretty kitten at 250 pixels wide, with 50% compression, you would write.
You can see the full list of options configurable by URL params, with examples, in the URL-Options document
We put a lot of defaults in place to prevent distortion, bad quality, weird cropping and unwanted padding.
The most common URL options are:
Description: Sets the target width of the image. If not set, width will be calculated in order to keep aspect ratio.
Description: Sets the target height of the image. If not set, height will be calculated in order to keep aspect ratio.
By default setting width and height together, works like defining a rectangle that will define a max-width and max-height and the image will scale proportionally to fit that area without cropping.
By default; width, height, or both will not scale up an image that is smaller than the defined dimensions.
Description: When both width and height are set, this allows the image to be cropped so it fills the width x height area.
Description: When crop is applied, changing the gravity will define which part of the image is kept inside the crop area. The basic options are:
Description: Apply image rotation (using shear operations) to the image.
Description: Output format requested, for example you can force the output as jpeg file in case of source file is png. The default
auto will try to output the best format for the requesting browser, falling back to the same format as the source image or finally with a fallback to jpg.
Description: Sets the compression level for the output image. Your best results will be between 70 and 95.
rf : refresh
Description: When this parameter is 1, it will force a re-request of the original image and run it through the transformations and compression again. It will delete the local cached copy.
There are some easy to setup server configurations in the
config/parameters.yml file, you can see the full list of options and server configurations in the Application Options Document
Restricted domains disabled by default. This means that you can fetch a resource from any URL. To enable the domain restriction, change in config/parameters.yml
After enabling, you need to put the white listed domains
whitelist_domains: - www.domain-1.org - www.domain-2.org
Based on this RFC Signature Generation was added to Flyimg in order to avoid DDOS attacks.
First you need to edit
security_iv in parameters.yml file and add a proper values.
Than any request to Fyimg app will throw an error unless it's encrypted.
To generate the encrypted url you need to run this command:
docker exec flyimg php app.php encrypt w_200,h_200,c_1/http://medula.cl/t/resize-test_1920.jpg
it'll return something like this:
Hashed request: TGQ1WWRKVGUrZUpoNmJMc2RMUENPL2t6ZDJkWkdOejlkM0p0U0F3WTgxOU5IMzF3U3R0d2V4b3dqbG52cFRTSFZDcmhrY1JnaGZYOHJ3V0NpZDNNRmc9PQ==
Now you can request the image throw this new url:
docker exec flyimg vendor/bin/phpunit
Generate Html Code Coverage
docker exec flyimg vendor/bin/phpunit --coverage-html build/html
- Server: nginx
- Application: Silex , a PHP micro-framework.
- Image manipulation: ImageMagick
- JPEG encoder: MozJpeg
- Storage: Flysystem
- Containerisation: Docker
Storage files based on Flysystem which is
a filesystem abstraction allows you to easily swap out a local filesystem for a remote one. Technical debt is reduced as is the chance of vendor lock-in.
Default storage is Local, but you can use other Adapters like AWS S3, Azure, FTP, DropBox, ...
Currently, only the local and S3 are implemented as Storage Provider in Flyimg application, but you can add your specific one easily in
src/Core/Provider/StorageProvider.php. Check an example for AWS S3 here.
See benchmark.sh for more details.
Crop http://localhost:8080/upload/w_200,h_200,c_1/Rovinj-Croatia.jpg Requests [total, rate] 500, 50.10 Duration [total, attack, wait] 9.991377689s, 9.97999997s, 11.377719ms Latencies [mean, 50, 95, 99, max] 19.402096ms, 12.844271ms, 54.65001ms, 96.276948ms, 135.597203ms Bytes In [total, mean] 5337500, 10675.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:500 Resize http://localhost:8080/upload/w_200,h_200,rz_1/Rovinj-Croatia.jpg Requests [total, rate] 500, 50.10 Duration [total, attack, wait] 9.992435445s, 9.979999871s, 12.435574ms Latencies [mean, 50, 95, 99, max] 16.676093ms, 12.376525ms, 49.676187ms, 97.354697ms, 127.14737ms Bytes In [total, mean] 3879500, 7759.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:500 Rotate http://localhost:8080/upload/r_-45,w_400,h_400/Rovinj-Croatia.jpg Requests [total, rate] 500, 50.10 Duration [total, attack, wait] 9.992650741s, 9.979999937s, 12.650804ms Latencies [mean, 50, 95, 99, max] 13.634143ms, 11.587252ms, 26.873827ms, 50.446923ms, 68.222253ms Bytes In [total, mean] 17609000, 35218.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:500
- Benchmark the application.
- Decouple the core logic from Silex in order to make it portable.
- Test it with couple of frameworks, Phalcon Php is a good candidate.
- Add overlays functionality (Text on top of the image)
- Storage auto-mapping
- Add support for FLIFF, BPG and JPEG2000
A special thanks to JetBrains for supporting our project with their open source license program.
Thank you to all our backers! [Become a backer]
Thank you to all our sponsors! (please ask your company to also support this open source project by becoming a sponsor)
The MIT License (MIT). Please see License File for more information.
Enjoy your Flyimaging!