Coder Social home page Coder Social logo

cshum / imagor Goto Github PK

View Code? Open in Web Editor NEW
3.2K 24.0 119.0 10.96 MB

Fast, secure image processing server and Go library, using libvips

License: Apache License 2.0

Go 92.15% Makefile 0.17% Dockerfile 0.58% C 7.10%
docker jpeg png libvips resize-images crop-image watermark gif webp avif golang

imagor's Introduction

imagor

Test Status Coverage Status Docker Hub GitHub Container Registry Go Reference

imagor is a fast, secure image processing server and Go library.

imagor uses one of the most efficient image processing library libvips. It is typically 4-8x faster than using the quickest ImageMagick and GraphicsMagick settings. imagor implements libvips streaming that facilitates parallel processing pipelines, achieving high network throughput.

imagor features a ton of image processing use cases, available as a HTTP server with first-class Docker support. It adopts the thumbor URL syntax representing a high-performance drop-in replacement.

imagor is a Go library built with speed, security and extensibility in mind. Alongside there is imagorvideo bringing video thumbnail capability through ffmpeg C bindings.

Quick Start

docker run -p 8000:8000 shumc/imagor -imagor-unsafe -imagor-auto-webp

Original images:

https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png
https://raw.githubusercontent.com/cshum/imagor/master/testdata/dancing-banana.gif
https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher-front.png

Try out the following image URLs:

http://localhost:8000/unsafe/fit-in/200x200/filters:fill(white)/https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png
http://localhost:8000/unsafe/200x200/smart/filters:fill(white):format(jpeg):quality(80)/https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png
http://localhost:8000/unsafe/fit-in/-180x180/10x10/filters:hue(290):saturation(100):fill(yellow)/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png
http://localhost:8000/unsafe/30x40:100x150/filters:fill(cyan)/raw.githubusercontent.com/cshum/imagor/master/testdata/dancing-banana.gif
http://localhost:8000/unsafe/fit-in/200x150/filters:fill(yellow):watermark(raw.githubusercontent.com/cshum/imagor/master/testdata/gopher-front.png,repeat,bottom,0,40,40)/raw.githubusercontent.com/cshum/imagor/master/testdata/dancing-banana.gif

Image Endpoint

imagor endpoint is a series of URL parts which defines the image operations, followed by the image URI:

/HASH|unsafe/trim/AxB:CxD/fit-in/stretch/-Ex-F/GxH:IxJ/HALIGN/VALIGN/smart/filters:NAME(ARGS):NAME(ARGS):.../IMAGE
  • HASH is the URL signature hash, or unsafe if unsafe mode is used
  • trim removes surrounding space in images using top-left pixel color
  • AxB:CxD means manually crop the image at left-top point AxB and right-bottom point CxD. Coordinates can also be provided as float values between 0 and 1 (percentage of image dimensions)
  • fit-in means that the generated image should not be auto-cropped and otherwise just fit in an imaginary box specified by ExF
  • stretch means resize the image to ExF without keeping its aspect ratios
  • -Ex-F means resize the image to be ExF of width per height size. The minus signs mean flip horizontally and vertically
  • GxH:IxJ add left-top padding GxH and right-bottom padding IxJ
  • HALIGN is horizontal alignment of crop. Accepts left, right or center, defaults to center
  • VALIGN is vertical alignment of crop. Accepts top, bottom or middle, defaults to middle
  • smart means using smart detection of focal points
  • filters a pipeline of image filter operations to be applied, see filters section
  • IMAGE is the image path or URI
    • For image URI that contains ? character, this will interfere the URL query and should be encoded with encodeURIComponent or equivalent

Filters

Filters /filters:NAME(ARGS):NAME(ARGS):.../ is a pipeline of image operations that will be sequentially applied to the image. Examples:

/filters:fill(white):format(jpeg)/
/filters:hue(290):saturation(100):fill(yellow):format(jpeg):quality(80)/
/filters:fill(white):watermark(raw.githubusercontent.com/cshum/imagor/master/testdata/gopher-front.png,repeat,bottom,10):format(jpeg)/

imagor supports the following filters:

  • background_color(color) sets the background color of a transparent image
    • color the color name or hexadecimal rgb expression without the “#” character
  • blur(sigma) applies gaussian blur to the image
  • brightness(amount) increases or decreases the image brightness
    • amount -100 to 100, the amount in % to increase or decrease the image brightness
  • contrast(amount) increases or decreases the image contrast
    • amount -100 to 100, the amount in % to increase or decrease the image contrast
  • fill(color) fill the missing area or transparent image with the specified color:
    • color - color name or hexadecimal rgb expression without the “#” character
      • If color is "blur" - missing parts are filled with blurred original image
      • If color is "auto" - the top left image pixel will be chosen as the filling color
      • If color is "none" - the filling would become fully transparent
  • focal(AxB:CxD) or focal(X,Y) adds a focal region or focal point for custom transformations:
    • Coordinated by a region of left-top point AxB and right-bottom point CxD, or a point X,Y.
    • Also accepts float values between 0 and 1 that represents percentage of image dimensions.
  • format(format) specifies the output format of the image
    • format accepts jpeg, png, gif, webp, tiff, avif, jp2
  • grayscale() changes the image to grayscale
  • hue(angle) increases or decreases the image hue
    • angle the angle in degree to increase or decrease the hue rotation
  • label(text, x, y, size, color[, alpha[, font]]) adds a text label to the image. It can be positioned inside the image with the alignment specified, color and transparency support:
    • text text label, also support url encoded text.
    • x horizontal position that the text label will be in:
      • Positive number indicate position from the left, negative number from the right.
      • Number followed by a p e.g. 20p means calculating the value from the image width as percentage
      • left,right,center align left, right or centered respectively
    • y vertical position that the text label will be in:
      • Positive number indicate position from the top, negative number from the bottom.
      • Number followed by a p e.g. 20p means calculating the value from the image height as percentage
      • top,bottom,center vertical align top, bottom or centered respectively
    • size - text label font size
    • color - color name or hexadecimal rgb expression without the “#” character
    • alpha - text label transparency, a number between 0 (fully opaque) and 100 (fully transparent).
    • font - text label font type
  • max_bytes(amount) automatically degrades the quality of the image until the image is under the specified amount of bytes
  • max_frames(n) limit maximum number of animation frames n to be loaded
  • orient(angle) rotates the image before resizing and cropping, according to the angle value
    • angle accepts 0, 90, 180, 270
  • page(num) specify page number for PDF, or frame number for animated image, starts from 1
  • dpi(num) specify the dpi to render at for PDF and SVG
  • proportion(percentage) scales image to the proportion percentage of the image dimension
  • quality(amount) changes the overall quality of the image, does nothing for png
    • amount 0 to 100, the quality level in %
  • rgb(r,g,b) amount of color in each of the rgb channels in %. Can range from -100 to 100
  • rotate(angle) rotates the given image according to the angle value
    • angle accepts 0, 90, 180, 270
  • round_corner(rx [, ry [, color]]) adds rounded corners to the image with the specified color as background
    • rx, ry amount of pixel to use as radius. ry = rx if ry is not provided
    • color the color name or hexadecimal rgb expression without the “#” character
  • saturation(amount) increases or decreases the image saturation
    • amount -100 to 100, the amount in % to increase or decrease the image saturation
  • sharpen(sigma) sharpens the image
  • strip_exif() removes Exif metadata from the resulting image
  • strip_icc() removes ICC profile information from the resulting image
  • upscale() upscale the image if fit-in is used
  • watermark(image, x, y, alpha [, w_ratio [, h_ratio]]) adds a watermark to the image. It can be positioned inside the image with the alpha channel specified and optionally resized based on the image size by specifying the ratio
    • image watermark image URI, using the same image loader configured for imagor
    • x horizontal position that the watermark will be in:
      • Positive number indicate position from the left, negative number from the right.
      • Number followed by a p e.g. 20p means calculating the value from the image width as percentage
      • left,right,center positioned left, right or centered respectively
      • repeat the watermark will be repeated horizontally
    • y vertical position that the watermark will be in:
      • Positive number indicate position from the top, negative number from the bottom.
      • Number followed by a p e.g. 20p means calculating the value from the image height as percentage
      • top,bottom,center positioned top, bottom or centered respectively
      • repeat the watermark will be repeated vertically
    • alpha watermark image transparency, a number between 0 (fully opaque) and 100 (fully transparent).
    • w_ratio percentage of the width of the image the watermark should fit-in
    • h_ratio percentage of the height of the image the watermark should fit-in

Utility Filters

These filters do not manipulate images but provide useful utilities to the imagor pipeline:

  • attachment(filename) returns attachment in the Content-Disposition header, and the browser will open a "Save as" dialog with filename. When filename not specified, imagor will get the filename from the image source
  • expire(timestamp) adds expiration time to the content. timestamp is the unix milliseconds timestamp, e.g. if content is valid for 30s then timestamp would be Date.now() + 30*1000 in JavaScript.
  • preview() skips the result storage even if result storage is enabled. Useful for conditional caching
  • raw() response with a raw unprocessed and unchecked source image. Image still loads from loader and storage but skips the result storage

Loader, Storage and Result Storage

imagor Loader, Storage and Result Storage are the building blocks for loading and saving images from various sources:

  • Loader loads image. Enable Loader where you wish to load images from, but without modifying it e.g. static directory.
  • Storage loads and saves image. This allows subsequent requests for the same image loads directly from the storage, instead of HTTP source.
  • Result Storage loads and saves the processed image. This allows subsequent request of the same parameters loads from the result storage, saving processing resources.

imagor provides built-in adaptors that support HTTP(s), Proxy, File System, AWS S3 and Google Cloud Storage. By default, HTTP Loader is used as fallback. You can choose to enable additional adaptors that fit your use cases.

File System

Docker Compose example with file system, using mounted volume:

version: "3"
services:
  imagor:
    image: shumc/imagor:latest
    volumes:
      - ./:/mnt/data
    environment:
      PORT: 8000
      IMAGOR_UNSAFE: 1 # unsafe URL for testing

      FILE_LOADER_BASE_DIR: /mnt/data # enable file loader by specifying base dir

      FILE_STORAGE_BASE_DIR: /mnt/data # enable file storage by specifying base dir
      FILE_STORAGE_MKDIR_PERMISSION: 0755 # optional
      FILE_STORAGE_WRITE_PERMISSION: 0666 # optional

      FILE_RESULT_STORAGE_BASE_DIR: /mnt/data/result # enable file result storage by specifying base dir
      FILE_RESULT_STORAGE_MKDIR_PERMISSION: 0755 # optional
      FILE_RESULT_STORAGE_WRITE_PERMISSION: 0666 # optional
      
    ports:
      - "8000:8000"

AWS S3

Docker Compose example with AWS S3. Also works with S3 compatible such as MinIO, DigitalOcean Space.

version: "3"
services:
  imagor:
    image: shumc/imagor:latest
    environment:
      PORT: 8000
      IMAGOR_SECRET: mysecret # secret key for URL signature
      AWS_ACCESS_KEY_ID: ...
      AWS_SECRET_ACCESS_KEY: ...
      AWS_REGION: ...

      S3_LOADER_BUCKET: mybucket # enable S3 loader by specifying bucket
      S3_LOADER_BASE_DIR: images # optional

      S3_STORAGE_BUCKET: mybucket # enable S3 storage by specifying bucket
      S3_STORAGE_BASE_DIR: images # optional
      S3_STORAGE_ACL: public-read # optional - see https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl

      S3_RESULT_STORAGE_BUCKET: mybucket # enable S3 result storage by specifying bucket
      S3_RESULT_STORAGE_BASE_DIR: images/result # optional
      S3_RESULT_STORAGE_ACL: public-read # optional
    ports:
      - "8000:8000"
Custom S3 Endpoint

Configure custom S3 endpoint for S3 compatible such as MinIO, DigitalOcean Space:

      S3_ENDPOINT: http://minio:9000
      S3_FORCE_PATH_STYLE: 1

By default, S3 prepends bucket name as subdomain to the request URL:

http://mybucket.minio:9000/image.jpg

this may not be desirable for a self-hosted endpoint. You can also switch to path-style requests using S3_FORCE_PATH_STYLE=1 such that the host remains unchanged:

http://minio:9000/mybucket/image.jpg
Different AWS Credentials for S3 Loader, Storage and Result Storage

Set the following environment variables to override the global AWS Credentials for S3 Loader, Storage and Result Storage:

AWS_LOADER_REGION
AWS_LOADER_ACCESS_KEY_ID
AWS_LOADER_SECRET_ACCESS_KEY
S3_LOADER_ENDPOINT

AWS_STORAGE_REGION
AWS_STORAGE_ACCESS_KEY_ID
AWS_STORAGE_SECRET_ACCESS_KEY
S3_STORAGE_ENDPOINT

AWS_RESULT_STORAGE_REGION
AWS_RESULT_STORAGE_ACCESS_KEY_ID
AWS_RESULT_STORAGE_SECRET_ACCESS_KEY
S3_RESULT_STORAGE_ENDPOINT

Google Cloud Storage

Docker Compose example with Google Cloud Storage:

version: "3"
services:
  imagor:
    image: shumc/imagor:latest
    volumes:
      - ./googlesecret:/etc/secrets/google
    environment:
      PORT: 8000
      IMAGOR_SECRET: mysecret # secret key for URL signature
      GOOGLE_APPLICATION_CREDENTIALS: /etc/secrets/google/appcredentials.json # google cloud secrets file

      GCLOUD_LOADER_BUCKET: mybucket # enable loader by specifying bucket
      GCLOUD_LOADER_BASE_DIR: images # optional

      GCLOUD_STORAGE_BUCKET: mybucket # enable storage by specifying bucket
      GCLOUD_STORAGE_BASE_DIR: images # optional
      GCLOUD_STORAGE_ACL: publicRead # optional - see https://cloud.google.com/storage/docs/json_api/v1/objects/insert

      GCLOUD_RESULT_STORAGE_BUCKET: mybucket # enable result storage by specifying bucket
      GCLOUD_RESULT_STORAGE_BASE_DIR: images/result # optional
      GCLOUD_RESULT_STORAGE_ACL: publicRead # optional
    ports:
      - "8000:8000"

Storage and Result Storage Path Style

Storage and Result Storage path style enables additional hashing rules to the storage path when loading and saving images:

IMAGOR_STORAGE_PATH_STYLE=digest

  • foobar.jpg becomes e6/86/1a810ff186b4f747ef85f7c53946f0e6d8cb

IMAGOR_RESULT_STORAGE_PATH_STYLE=digest

  • fit-in/16x17/foobar.jpg becomes 61/4c/9ba1725e8cdd8263a4ad437c56b35f33deba

IMAGOR_RESULT_STORAGE_PATH_STYLE=suffix

  • 166x169/top/foobar.jpg becomes foobar.45d8ebb31bd4ed80c26e.jpg
  • 17x19/smart/example.com/foobar becomes example.com/foobar.ddd349e092cda6d9c729

IMAGOR_RESULT_STORAGE_PATH_STYLE=size

  • 166x169/top/foobar.jpg becomes foobar.45d8ebb31bd4ed80c26e_166x169.jpg
  • 17x19/smart/example.com/foobar becomes example.com/foobar.ddd349e092cda6d9c729_17x19

Security

URL Signature

In production environment, it is highly recommended turning off IMAGOR_UNSAFE and setting up URL signature using IMAGOR_SECRET, to prevent DDoS attacks that abuse multiple image operations.

The URL signature hash is based on SHA digest, created by taking the URL path (excluding /unsafe/) with secret. The hash is then Base64 URL encoded. An example in Node.js:

const crypto = require('crypto');

function sign(path, secret) {
  const hash = crypto.createHmac('sha1', secret)
          .update(path)
          .digest('base64')
          .replace(/\+/g, '-').replace(/\//g, '_')
  return hash + '/' + path
}

console.log(sign('500x500/top/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png', 'mysecret'))
// cST4Ko5_FqwT3BDn-Wf4gO3RFSk=/500x500/top/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png

Custom HMAC Signer

imagor uses SHA1 HMAC signer by default, the same one used by thumbor. However, SHA1 is not considered cryptographically secure. If that is a concern it is possible to configure different signing method and truncate length. imagor supports sha1, sha256, sha512 signer type:

IMAGOR_SIGNER_TYPE=sha256
IMAGOR_SIGNER_TRUNCATE=40

The Node.js example then becomes:

const crypto = require('crypto');

function sign(path, secret) {
  const hash = crypto.createHmac('sha256', secret)
          .update(path)
          .digest('base64')
          .slice(0, 40)
          .replace(/\+/g, '-').replace(/\//g, '_')
  return hash + '/' + path
}

console.log(sign('500x500/top/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png', 'mysecret'))
// IGEn3TxngivD0jy4uuiZim2bdUCvhcnVi1Nm0xGy/500x500/top/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png

Image Bombs Prevention

imagor checks the image type and its resolution before the actual processing happens. The processing will be rejected if the image dimensions are too big, which protects from so-called "image bombs". You can set the max allowed image resolution and dimensions using VIPS_MAX_RESOLUTION, VIPS_MAX_WIDTH, VIPS_MAX_HEIGHT:

VIPS_MAX_RESOLUTION=16800000
VIPS_MAX_WIDTH=5000
VIPS_MAX_HEIGHT=5000

Allowed Sources and Base URL

Whitelist specific hosts to restrict loading images only from the allowed sources using HTTP_LOADER_ALLOWED_SOURCES or HTTP_LOADER_ALLOWED_SOURCE_REGEXP.

  • HTTP_LOADER_ALLOWED_SOURCES accepts csv wth glob pattern e.g.:

    HTTP_LOADER_ALLOWED_SOURCES=*.foobar.com,my.foobar.com,mybucket.s3.amazonaws.com
  • HTTP_LOADER_ALLOWED_SOURCE_REGEXP accepts a regular expression matching on the full URL e.g.:

    HTTP_LOADER_ALLOWED_SOURCE_REGEXP='^https://raw\.githubusercontent\.com/cshum/imagor/.*'

Alternatively, it is possible to set a base URL for loading images strictly from one HTTP source. This also trims down the base URL from image endpoint:

Example URL:

http://localhost:8000/unsafe/fit-in/200x150/filters:fill(yellow):watermark(raw.githubusercontent.com/cshum/imagor/master/testdata/gopher-front.png,repeat,bottom,0,40,40)/raw.githubusercontent.com/cshum/imagor/master/testdata/dancing-banana.gif

With HTTP Loader Base URL config:

HTTP_LOADER_BASE_URL=https://raw.githubusercontent.com/cshum/imagor/master

The example URL then becomes:

http://localhost:8000/unsafe/fit-in/200x150/filters:fill(yellow):watermark(testdata/gopher-front.png,repeat,bottom,0,40,40)/testdata/dancing-banana.gif

Metadata and Exif

imagor provides metadata endpoint that extracts information such as image format, resolution and Exif metadata. Under the hood, it tries to retrieve data just enough to extract the header, without reading and processing the whole image in memory.

To use the metadata endpoint, add /meta right after the URL signature hash before the image operations. Example:

http://localhost:8000/unsafe/meta/fit-in/50x50/raw.githubusercontent.com/cshum/imagor/master/testdata/Canon_40D.jpg
{
  "format": "jpeg",
  "content_type": "image/jpeg",
  "width": 50,
  "height": 34,
  "orientation": 1,
  "pages": 1,
  "bands": 3,
  "exif": {
    "ApertureValue": "368640/65536",
    "ColorSpace": 1,
    "ComponentsConfiguration": "Y Cb Cr -",
    "Compression": 6,
    "DateTime": "2008:07:31 10:38:11",
    "ISOSpeedRatings": 100,
    "Make": "Canon",
    "MeteringMode": 5,
    "Model": "Canon EOS 40D",
    //...
  }
}

Prepending /params to the existing endpoint returns the endpoint attributes in JSON form, useful for previewing the endpoint parameters. Example:

curl 'http://localhost:8000/params/g5bMqZvxaQK65qFPaP1qlJOTuLM=/fit-in/500x400/0x20/filters:fill(white)/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png'

Go Library

imagor is a Go library built with speed, security and extensibility in mind. It facilitates high-level image processing in a modular architecture made up of a series of Go packages:

  • imagor - the imagor core library
  • imagorpath - parse and generate imagor endpoint
  • vips - libvips C bindings with imagor.Processor implementation
  • httploader - HTTP Loader, an imagor.Loader implementation
  • filestorage - File Storage, an imagor.Storage implementation
  • s3storage - AWS S3 Storage, an imagor.Storage implementation
  • gcloudstorage - Google Cloud Storage, an imagor.Storage implementation

Install libvips and enable CGO:

  • brew install vips for Mac
  • CGO_CFLAGS_ALLOW=-Xpreprocessor being set to compile Go

See example below and also examples folder for various ways you can use imagor:

package main

import (
	"context"
	"github.com/cshum/imagor"
	"github.com/cshum/imagor/imagorpath"
	"github.com/cshum/imagor/loader/httploader"
	"github.com/cshum/imagor/vips"
	"io"
	"os"
)

func main() {
	app := imagor.New(
		imagor.WithLoaders(httploader.New()),
		imagor.WithProcessors(vips.NewProcessor()),
	)
	ctx := context.Background()
	if err := app.Startup(ctx); err != nil {
		panic(err)
	}
	defer app.Shutdown(ctx)
	blob, err := app.Serve(ctx, imagorpath.Params{
		Image:  "https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png",
		Width:  500,
		Height: 500,
		Smart:  true,
		Filters: []imagorpath.Filter{
			{"fill", "white"},
			{"format", "jpg"},
		},
	})
	if err != nil {
		panic(err)
	}
	reader, _, err := blob.NewReader()
	if err != nil {
		panic(err)
	}
	defer reader.Close()
	file, err := os.Create("gopher.jpg")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	if _, err := io.Copy(file, reader); err != nil {
		panic(err)
	}
}

Configuration

imagor supports command-line arguments and environment variables for the arguments equivalent in capitalized snake case, see available options imagor -h. For instances -imagor-secret would become IMAGOR_SECRET:

# both are equivalent

imagor -debug -imagor-secret 1234

DEBUG=1 IMAGOR_SECRET=1234 imagor

Configuration can also be specified in a .env environment variable file and referenced with the -config flag:

imagor -config path/to/config.env

config.env:

PORT=8000
IMAGOR_SECRET=mysecret
DEBUG=1

Available options

imagor -h
Usage of imagor:
  -debug
        Debug mode
  -port int
        Server port (default 8000)
  -version
        imagor version
  -config string
        Retrieve configuration from the given file (default ".env")

  -imagor-secret string
        Secret key for signing imagor URL
  -imagor-unsafe
        Unsafe imagor that does not require URL signature. Prone to URL tampering
  -imagor-auto-webp
        Output WebP format automatically if browser supports
  -imagor-auto-avif
        Output AVIF format automatically if browser supports (experimental)
  -imagor-base-params string
        imagor endpoint base params that applies to all resulting images e.g. filters:watermark(example.jpg)
  -imagor-signer-type string
        imagor URL signature hasher type: sha1, sha256, sha512 (default "sha1")
  -imagor-signer-truncate int
        imagor URL signature truncate at length
  -imagor-result-storage-path-style string
        imagor result storage path style: original, digest, suffix (default "original")
  -imagor-storage-path-style string
        imagor storage path style: original, digest (default "original")
  -imagor-cache-header-ttl duration
        imagor HTTP cache header ttl for successful image response (default 168h0m0s)
  -imagor-cache-header-swr duration
        imagor HTTP Cache-Control header stale-while-revalidate for successful image response (default 24h0m0s)
  -imagor-cache-header-no-cache
        imagor HTTP Cache-Control header no-cache for successful image response
  -imagor-request-timeout duration
        Timeout for performing imagor request (default 30s)
  -imagor-load-timeout duration
        Timeout for imagor Loader request, should be smaller than imagor-request-timeout
  -imagor-save-timeout duration
        Timeout for saving image to imagor Storage
  -imagor-process-timeout duration
        Timeout for image processing
  -imagor-process-concurrency int
        Maximum number of image process to be executed simultaneously. Requests that exceed this limit are put in the queue. Set -1 for no limit (default -1)
  -imagor-process-queue-size int
        Maximum number of image process that can be put in the queue. Requests that exceed this limit are rejected with HTTP status 429
  -imagor-base-path-redirect string
        URL to redirect for imagor / base path e.g. https://www.google.com
  -imagor-modified-time-check
        Check modified time of result image against the source image. This eliminates stale result but require more lookups
  -imagor-disable-params-endpoint
        imagor disable /params endpoint
  -imagor-disable-error-body
        imagor disable response body on error

  -server-address string
        Server address
  -server-cors
        Enable CORS
  -server-strip-query-string
        Enable strip query string redirection
  -server-path-prefix string
        Server path prefix
  -server-access-log
        Enable server access log

  -prometheus-bind string
        Specify address and port to enable Prometheus metrics, e.g. :5000, prom:7000
  -prometheus-path string
        Prometheus metrics path (default "/")
        
  -http-loader-allowed-sources string
        HTTP Loader allowed hosts whitelist to load images from if set. Accept csv wth glob pattern e.g. *.google.com,*.github.com.
  -http-loader-base-url string
        HTTP Loader base URL that prepends onto existing image path. This overrides the default scheme option.
  -http-loader-forward-headers string
        Forward request header to HTTP Loader request by csv e.g. User-Agent,Accept
  -http-loader-override-response-headers string
        Override HTTP Loader response header to image response by csv e.g. Cache-Control,Expires
  -http-loader-forward-client-headers
        Forward browser client request headers to HTTP Loader request
  -http-loader-insecure-skip-verify-transport
        HTTP Loader to use HTTP transport with InsecureSkipVerify true
  -http-loader-max-allowed-size int
        HTTP Loader maximum allowed size in bytes for loading images if set
  -http-loader-proxy-urls string
        HTTP Loader Proxy URLs. Enable HTTP Loader proxy only if this value present. Accept csv of proxy urls e.g. http://user:pass@host:port,http://user:pass@host:port
  -http-loader-allowed-source-regexp string
        HTTP Loader allowed hosts regexp to load images from if set. Combines as OR with allowed host glob pattern sources.
  -http-loader-proxy-allowed-sources string
        HTTP Loader Proxy allowed hosts that enable proxy transport, if proxy URLs are set. Accept csv wth glob pattern e.g. *.google.com,*.github.com.
  -http-loader-default-scheme string
        HTTP Loader default scheme if not specified by image path. Set "nil" to disable default scheme. (default "https")
  -http-loader-accept string
        HTTP Loader set request Accept header and validate response Content-Type header (default "*/*") 
  -http-loader-block-link-local-networks
        HTTP Loader rejects connections to link local network IP addresses.
  -http-loader-block-loopback-networks
        HTTP Loader rejects connections to loopback network IP addresses.
  -http-loader-block-private-networks
        HTTP Loader rejects connections to private network IP addresses.
  -http-loader-block-networks string
        HTTP Loader rejects connections to link local network IP addresses. This options takes a comma separated list of networks in CIDR notation e.g ::1/128,127.0.0.0/8.
  -http-loader-disable
        Disable HTTP Loader

  -file-safe-chars string
        File safe characters to be excluded from image key escape. Set -- for no-op
  -file-loader-base-dir string
        Base directory for File Loader. Enable File Loader only if this value present
  -file-loader-path-prefix string
        Base path prefix for File Loader
  -file-result-storage-base-dir string
        Base directory for File Result Storage. Enable File Result Storage only if this value present
  -file-result-storage-mkdir-permission string
        File Result Storage mkdir permission (default "0755")
  -file-result-storage-path-prefix string
        Base path prefix for File Result Storage
  -file-result-storage-write-permission string
        File Storage write permission (default "0666")
  -file-result-storage-expiration duration
        File Result Storage expiration duration e.g. 24h. Default no expiration
  -file-storage-base-dir string
        Base directory for File Storage. Enable File Storage only if this value present
  -file-storage-path-prefix string
        Base path prefix for File Storage
  -file-storage-mkdir-permission string
        File Storage mkdir permission (default "0755")
  -file-storage-write-permission string
        File Storage write permission (default "0666")
  -file-storage-expiration duration
        File Storage expiration duration e.g. 24h. Default no expiration

  -aws-access-key-id string
        AWS Access Key ID. Required if using S3 Loader or S3 Storage
  -aws-region string
        AWS Region. Required if using S3 Loader or S3 Storage
  -aws-secret-access-key string
        AWS Secret Access Key. Required if using S3 Loader or S3 Storage
  -aws-session-token string
        AWS Session Token. Optional temporary credentials token
  -s3-endpoint string
        Optional S3 Endpoint to override default
  -s3-safe-chars string
        S3 safe characters to be excluded from image key escape. Set -- for no-op
  -s3-force-path-style
        S3 force the request to use path-style addressing s3.amazonaws.com/bucket/key, instead of bucket.s3.amazonaws.com/key
  -s3-loader-bucket string
        S3 Bucket for S3 Loader. Enable S3 Loader only if this value present
  -s3-loader-base-dir string
        Base directory for S3 Loader
  -s3-loader-path-prefix string
        Base path prefix for S3 Loader
  -s3-result-storage-bucket string
        S3 Bucket for S3 Result Storage. Enable S3 Result Storage only if this value present
  -s3-result-storage-base-dir string
        Base directory for S3 Result Storage
  -s3-result-storage-path-prefix string
        Base path prefix for S3 Result Storage
  -s3-result-storage-acl string
        Upload ACL for S3 Result Storage (default "public-read")
  -s3-result-storage-expiration duration
        S3 Result Storage expiration duration e.g. 24h. Default no expiration
  -s3-storage-bucket string
        S3 Bucket for S3 Storage. Enable S3 Storage only if this value present
  -s3-storage-base-dir string
        Base directory for S3 Storage
  -s3-storage-path-prefix string
        Base path prefix for S3 Storage
  -s3-storage-acl string
        Upload ACL for S3 Storage (default "public-read")
  -s3-storage-expiration duration
        S3 Storage expiration duration e.g. 24h. Default no expiration
        
  -aws-loader-access-key-id string
        AWS Access Key ID for S3 Loader to override global config
  -aws-loader-region string
        AWS Region for S3 Loader to override global config
  -aws-loader-secret-access-key string
        AWS Secret Access Key for S3 Loader to override global config
  -aws-loader-session-token string
        AWS Session Token for S3 Loader to override global config
  -s3-loader-endpoint string
        Optional S3 Loader Endpoint to override default
  -aws-storage-access-key-id string
        AWS Access Key ID for S3 Storage to override global config
  -aws-storage-region string
        AWS Region for S3 Storage to override global config
  -aws-storage-secret-access-key string
        AWS Secret Access Key for S3 Storage to override global config
  -aws-storage-session-token string
        AWS Session Token for S3 Storage to override global config
  -s3-storage-endpoint string
        Optional S3 Storage Endpoint to override default
  -aws-result-storage-access-key-id string
        AWS Access Key ID for S3 Result Storage to override global config
  -aws-result-storage-region string
        AWS Region for S3 Result Storage to override global config
  -aws-result-storage-secret-access-key string
        AWS Secret Access Key for S3 Result Storage to override global config
  -aws-result-storage-session-token string
        AWS Session Token for S3 Result Storage to override global config
  -s3-result-storage-endpoint string
        Optional S3 Storage Endpoint to override default

  -gcloud-safe-chars string
        Google Cloud safe characters to be excluded from image key escape. Set -- for no-op
  -gcloud-loader-base-dir string
        Base directory for Google Cloud Loader
  -gcloud-loader-bucket string
        Bucket name for Google Cloud Storage Loader. Enable Google Cloud Loader only if this value present
  -gcloud-loader-path-prefix string
        Base path prefix for Google Cloud Loader
  -gcloud-result-storage-acl string
        Upload ACL for Google Cloud Result Storage
  -gcloud-result-storage-base-dir string
        Base directory for Google Cloud Result Storage
  -gcloud-result-storage-bucket string
        Bucket name for Google Cloud Result Storage. Enable Google Cloud Result Storage only if this value present
  -gcloud-result-storage-expiration duration
        Google Cloud Result Storage expiration duration e.g. 24h. Default no expiration
  -gcloud-result-storage-path-prefix string
        Base path prefix for Google Cloud Result Storage
  -gcloud-storage-acl string
        Upload ACL for Google Cloud Storage
  -gcloud-storage-base-dir string
        Base directory for Google Cloud
  -gcloud-storage-bucket string
        Bucket name for Google Cloud Storage. Enable Google Cloud Storage only if this value present
  -gcloud-storage-expiration duration
        Google Cloud Storage expiration duration e.g. 24h. Default no expiration
  -gcloud-storage-path-prefix string
        Base path prefix for Google Cloud Storage
        
  -vips-max-animation-frames int
        VIPS maximum number of animation frames to be loaded. Set 1 to disable animation, -1 for unlimited
  -vips-disable-blur
        VIPS disable blur operations for vips processor
  -vips-disable-filters string
        VIPS disable filters by csv e.g. blur,watermark,rgb
  -vips-max-filter-ops int
        VIPS maximum number of filter operations allowed. Set -1 for unlimited (default -1)
  -vips-max-width int
        VIPS max image width
  -vips-max-height int
        VIPS max image height
  -vips-max-resolution int
        VIPS max image resolution
  -vips-mozjpeg
        VIPS enable maximum compression with MozJPEG. Requires mozjpeg to be installed

imagor's People

Contributors

asafamr-mm avatar bcspragu avatar blesswinsamuel avatar chakflying avatar cshum avatar dangkaka avatar dependabot[bot] avatar eyepulp avatar f100024 avatar headegg avatar jvahldick avatar kkdai avatar lorenries avatar nestor-sk avatar nicholascapo avatar p2004a avatar pletessier avatar srlk avatar testwill avatar thomasf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imagor's Issues

Clarification for different storage buckets

I don't quite understand what the difference between result-loader and result-storage would be? Can this be the same bucket?
The same applies to loader and storage. Is this to guard against deletion in the loader bucket?

In my simple mind the server would load something and then serve a result (optionally storing it). Thus I don't see where exactly the other two buckets come into play.

Thank you for your great work

confusing 404 warnings

I just setup imagor and it's working great, but I see a lot of log lines like this

{"level":"warn","caller":"imagor/imagor.go:305","ts":1646775438.21022,"key":"0x241:5017x3165/326x190/filters:strip_exif()/<escaped_http_url>","msg":"load","error":"imagor: 404 not found"}

Shortly after I see the 200 response in the access log

{"status":200,"ts":1646775438.541976,"level":"info","ip":"xxx","caller":"server/handler.go:98","took":0.331850614,"uri":"/unsafe/0x241:5017x3165/326x190/filters:strip_exif()/<escaped_http_url>","msg":"access","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:37.0) Gecko/20100101 Firefox/37.0","method":"GET"}

I assume the 404 is coming from the result storage? I'm running with

-file-result-storage-base-dir=/tmp -file-result-storage-expiration=1h

My intent with the result storage is to use it as a short-term cache, so misses are expected. Is there a way to silence this message?

Also, if my assumption is correct, using a "404 not found" message for a file not found on the filesystem was confusing and somewhat alarming for images I knew should be returning a 200. It would be nice to have a message that told me it didn't exist on the filesystem instead (maybe with the full path?).

fill filter compatibility with thumbor?

Excited to see imagor ! We're pretty heavy users of thumbor, and I also maintain the docker versions of thumbor (+ caching layer) at https://github.com/MinimalCompact/thumbor/

I took imagor for a quick spin as a drop-in replacement, and it seems to work quite well out of the box. One thing I already found however. It seems like the fill filter isn't entirely compatible with thumbor?

We have a URL that looks ilke this /{hash}/fit-in/205x205/filters:fill(FFFFFF,true):format(jpeg)/path/to/image.png. With thumbor, the background is white, whereas it is black with imagor

Would we need to change the fill params? or are you guys planning on making it compatible with thumbor?

Security around image bombs

Thank you for your amazing work on this project! Was wondering if there's anything addressing or designing around mallicious images? Imgproxy mentions it here: https://github.com/imgproxy/imgproxy#security

imgproxy checks the image type and its “real” dimensions when downloading. The image will not be fully downloaded if it has an unknown format or if the dimensions are too big (you can set the max allowed dimensions). This is how imgproxy protects from so called "image bombs”

Thank you

Compatibility with Google Cloud Storage

Hi, just found this library and it looks like a great alternative to imaginary.

Just wondering if I'm able to swap out S3 storage with Google Cloud Storage.

From the docs, I'm guessing it's not possible right now because you're putting just the bucket name

watermark position

followup from #16

http://localhost:8000/unsafe/fit-in/1200x800/filters:fill(yellow):watermark(https://i.imgur.com/71LaQJU.png,-10,-10,0):format(jpeg)/upload.wikimedia.org/wikipedia/commons/a/a9/St_Francis_Seminary.jpg

using thumbor:

St_Francis_Seminary thumbor

using imagor:

St_Francis_Seminary imagor

The image on Docker Hub is not multi-arch.

But the GitHub Actions workflow appears to be attempting to build and push a multi-arch image.

image

image

Perhaps the config is building both platforms with the same tag and overwriting the arm64 image with the amd64 image, instead of pushing two separate images with a multi-arch manifest?

Imagor stops working after a few days

We've been using imagor in production . After some days (undetermined, maybe 5 or maybe 2) it stops giving 408 status for any image that is not in the cache or result cache (cached images work fine). I haven't seen any other warnings or errors. It starts working normally after a container restart.

Process memory was at about 380M before last restart, I don't think memory is the problem here. System load also looks fine. I've also checked that the images do load from that host.

Where can I look at? I can't enable debug since it's production and it happens after many hours of use

This is the docker-compose conf (it was working with 0.8.13 since yesterday):

version: '3'
services:
  imagor:
    container_name: imagor
    image: shumc/imagor:0.8.15
    restart: unless-stopped
    environment:
      IMAGOR_UNSAFE: 1
      VIPS_MAX_WIDTH: 2500
      VIPS_MAX_HEIGHT: 2500
      VIPS_MAX_ANIMATION_FRAMES: 1
      VIPS_CONCURRENCY: 12
      IMAGOR_PROCESS_CONCURRENCY: 12
      IMAGOR_REQUEST_TIMEOUT: 15s
      IMAGOR_LOAD_TIMEOUT: 12s
      IMAGOR_SAVE_TIMEOUT: 8s
      IMAGOR_PROCESS_TIMEOUT: 8s
      FILE_STORAGE_BASE_DIR: /data/storage
      FILE_STORAGE_EXPIRATION: 240h
      FILE_RESULT_STORAGE_BASE_DIR: /data/result-storage
      FILE_RESULT_STORAGE_EXPIRATION: 240h
      SERVER_ACCESS_LOG: 1
    volumes:
      - /imagor:/data
    ports:
      - 8088:8000
    logging:
      options:
        max-size: 10m

Here is the log:

{"level":"info","ts":1648990752.961404,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/480x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fe5aba067_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.001026158}
{"level":"info","ts":1648990752.97457,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/480x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F12057946_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000918045}
{"level":"info","ts":1648990752.9747136,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/480x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F2fbcadc9_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000146382}
{"level":"info","ts":1648990752.9756482,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fa36005b9_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.00070613}
{"level":"info","ts":1648990752.9778147,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F0a85c357_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000178921}
{"level":"info","ts":1648990752.9778247,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/480x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Ffab73574_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.001116044}
{"level":"info","ts":1648990752.9778512,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F8d3795f7_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000416675}
{"level":"info","ts":1648990752.9778626,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fae5027ef_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000943989}
{"level":"info","ts":1648990752.977896,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F56f5e24e_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.00055589}
{"level":"info","ts":1648990752.9778981,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F0f292ddf_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.001085486}
{"level":"info","ts":1648990752.977921,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F2762b899_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000723866}
{"level":"info","ts":1648990752.9779384,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F624a2897_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000044393}
{"level":"info","ts":1648990752.979009,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F6b5d6976_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000531184}
{"level":"info","ts":1648990752.979026,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fc9b02d54_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000618871}
{"level":"info","ts":1648990752.979019,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F37552625_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000116353}
{"level":"info","ts":1648990752.9790409,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fw3839h2554x0y0-5fb828c1_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000258686}
{"level":"info","ts":1648990752.98013,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F6e80c4d3_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000358459}
{"level":"info","ts":1648990752.9801385,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F0813f02a_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000672276}
{"level":"info","ts":1648990752.980162,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fd956af88_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000455944}
{"level":"info","ts":1648990752.981225,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F67b1479a_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000981561}
{"level":"info","ts":1648990752.9812527,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F3fc9e583_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000999404}
{"level":"info","ts":1648990752.9813356,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F75496e68_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000046782}
{"level":"info","ts":1648990752.9813657,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F3ebf1254_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000071378}
{"level":"info","ts":1648990752.9813797,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F56af8140_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000087917}
{"level":"info","ts":1648990752.9823616,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fa9027343_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000345702}
{"level":"info","ts":1648990752.9823604,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Ff06c20f0_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000557614}
{"level":"info","ts":1648990752.982379,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F2fbcadc9_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000870566}
{"level":"info","ts":1648990752.9823644,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F025f3466_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000773003}
{"level":"info","ts":1648990752.9824026,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Faa73ad0f_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000748786}
{"level":"info","ts":1648990752.984616,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fa7a4a11c_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000802961}
{"level":"info","ts":1648990752.9867806,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2Fb85c17b0_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.00060075}
{"level":"info","ts":1648990752.9909296,"caller":"server/handler.go:98","msg":"access","status":408,"method":"GET","uri":"/unsafe/fit-in/2000x0/filters:quality(75)/https%3A%2F%2Fi.travelapi.com%2Fhotels%2F2000000%2F1130000%2F1121700%2F1121636%2F24077b87_z.jpg","ip":"37.133.66.93","user-agent":"Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/99.0.4844.59 Mobile/15E148 Safari/604.1","took":15.000794574}


http-loader does not forward http host header despite `-http-loader-forward-all-headers`

We want imagor to fetch images via internal network to make sure to avoid cdn optimizations and really load the original image.

The upstream server has multiple vhosts configured to separate images for different domains:

10.1.1.1
vhost img.example.com -> /var/www/img.example.com/
vhost img.another.net -> /var/www/img.antoher.net/

We enabled -http-loader-forward-all-headers and tried requests like this:

curl -H "Host: img.another.net" -H "Foo: bar" http://<imagor_ip>/unsafe/fit-in/800x600/http://10.1.1.1/pic.jpg

This will produce the following request on the upstream server (which we tested with netcat -ltp80)

GET /pic.jpg HTTP/1.1
Host: 10.1.1.1
User-Agent: curl/7.58.0
Accept: */*
Foo: bar

As you can see the Foo-Header will be passed along due to -http-loader-forward-all-headers but the Host-Header is not.

This can be fixed with a /etc/hosts entry for each domain:

10.1.1.1 img.example.com
10.1.1.1 img.another.net

But i think it's at least misleading that -http-loader-forward-all-headers does not really forward all headers.

PDF render support

Can Imagor support PDF file processing, is it possible to render specific page of a PDF?

Rotate filter applied too late compared to other image manipulation

I tried to use the rotate filter with a crop.
With a value of 180 degree, it works perfectly, but with a 90 degree rotation I obtained a squared image.

As you can see, rotation is correctly applied, but the crop with final dimensions is already applied, which results in generating a square.
It seems rotation is applied too late.

Here is the original image I used : http://coyote.vtt.free.fr/vw/MireCouleur16-9.jpg

180 degrees rotation:

http://localhost:8000/unsafe/125x772/filters:rotate(180):fill(white)/http://coyote.vtt.free.fr/vw/MireCouleur16-9.jpg

here is the result:
180


90 degrees rotation:

http://localhost:8000/unsafe/125x772/filters:rotate(90):fill(white)/http://coyote.vtt.free.fr/vw/MireCouleur16-9.jpg

here is the result:
90

How to activate file saving?

Great tool!
The only question I have is how to activate file saving: I have defined the FILE_STORAGE_BASE_DIR in the docker-compose.yml file yet nothing happens when triggering the image treatments from the browser.

compression?

Hey @cshum it's me again :)

This is more of a request/wish than anything, but I'd be curious if it's possible to push the limits a bit... I wonder if the quality filter or some other potential compression filter can be applied to make images smaller?

For example, I took the gopher image from the examples using

http://localhost:8000/unsafe/filters:hue(290):saturation(100):fill(yellow):format(jpeg):quality(80)/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png

and I get this image (136kb)

gopher

If I then compress it with tinypng.com it compresses it to 104kb ... not sure if it's lossy or lossless though

gopher tinypng

I played around with some of our images, and the difference in size can be even bigger...

Is it purely a question of fine-tuning the quality? or is there some other compression that can be used?

ffmpeg backend, thoughts?

So I've noticed Thumbor has https://github.com/theatlantic/thumbor-video-engine

Was wondering if @cshum has any thoughts on this, or if there is any ongoing effort to add ffmpeg as a backend (or something else that can do video, alongside vips) and/or what steps would be involved to do this?

Would be crazy useful to have the same basic interface with some different options for video.

Could just call whatever version of ffmpeg is on the host to Keep It Simple. 🤔

Thanks and keep up the amazing work.

Question :: Support to image borders

Hi there,
First of all, congrats on the project. It seems really promising.

I am currently trying to add borders to the image, however, it seems that the project does not support it right now.
Is there any plan on including it? or do you know what is the best way to achieve it?

I tried a couple of possibilities to achieve it with a single URL such as

  • using padding and applying "round corner"
  • applying filters multiple times
    /filters:round_corner(310,310,ff0000)/filters:round_corner(305:305)
    /filters:round_corner(310,310,ff0000):round_corner(305:305)

For some reason, the padding does not seem to work fully with the crop/round corner.

/healthcheck returns 403

Not sure if I'm doing something wrong here or if this is a bug:

$ docker run --rm --detach -p 8888:8888 shumc/imagor:0.8.11 -imagor-unsafe -port=8888
0a30471e0f1175e2cee52665651bda2d709321b77cdce192c7f3dda5d5112456
$ curl -I localhost:8888/healthcheck
HTTP/1.1 403 Forbidden
Date: Tue, 08 Mar 2022 18:58:28 GMT
Content-Length: 49
Content-Type: text/plain; charset=utf-8

406 VipsJpeg: Premature end of input file

Hi, I recently upgraded the imagor to v1.0.0 and I saw this error in docker container log many times for different images:

{
    "level": "warn",
    "ts": 1660161069.1208205,
    "caller": "imagor/imagor.go:326",
    "msg": "process",
    "params": {
        "path": "fit-in/1280x720/filters:format(webp)/xxxx",
        "image": "xxxx",
        "hash": "xxxxx",
        "fit_in": true,
        "width": 1280,
        "height": 720,
        "filters": [
            {
                "name": "format",
                "args": "webp"
            }
        ]
    },
    "error": "imagor: 406 VipsJpeg: Premature end of input file"
}

possible optimisation

Hello, one of the libvips maintainers here, congrats on this nice project!

I had a very quick look through the code (perhaps I missed it) but I'm not sure you're using Thumbnail on a non-image source, and this means (I think) you're not exploiting shrink-on-load. This can give very nice performance improvements, especially with JPEG images.

For example, theo.jpg is a 6k x 4k jpg image. If I resize with vips_resize() I see:

$ /usr/bin/time -f %M:%e vips resize theo.jpg x.jpg 0.033
88048:0.30

So 90MB of memory and 300ms, but if I resize with vips_thumbnail() it's:

$ /usr/bin/time -f %M:%e vips thumbnail theo.jpg x.jpg 200
40304:0.06

40MB and 60ms -- five times faster and half the memory use.

The vips_thumbnail() and vips_thumbnail_buffer() operations resize from a file, or from a memory area containing the encoded image, combining open and resize in one operation. Because open knows the final desired size, it can exploit tricks in many image format libraries (jpg, webp, jp2k, tiff, svg, pdf. openslide, etc.). It's well worth reorganising your code to hit these fast paths if you can.

I also noticed you're not premultiplying before resize (again, perhaps I missed it). You'll probably see haloing in transparent elements in PNG images. The fix is to premultiply / resize / unpremultiply in this case. Again, the thumbnail operations will handle this for you.

Benchmarks

Hi again :)

I'm trying to run some simple benchmarks to compare thumbor performance to imagor. For some reason I'm not sure the VIPS_CONCURRENCY_LEVEL env has an effect? It seems like it's always using all available CPUs ... Also interesting is that whilst it seems to run faster compared it to a single-process thumbor, when I run it with concurrency of 4 against thumbor (with the same concurrency), thumbor out-performs imagor (without caching, only keeping the original image in file storage) ...

Perhaps it's due to the way I've setup the benchmark? maybe it doesn't keep the original image in storage and always fetches it? or some other trivial misconfiguration? because otherwise I would expect libvips to outperform thumbor ...

You can see the benchmark code I'm running at https://github.com/MinimalCompact/thumbor/tree/imagor-thumbor-benchmark/benchmarks/locust (it's based on an older benchmark I created for optimizing thumbor on a multi-proc environment, so it's a bit messy unfortunately, but hope it's still useful)

I tested it on a DigitalOcean droplet with 4 cpus. Here's a sheet with the stats

Access rights considerations in a docker installation

I have an image present at the right location (I checked), with the right authorizations, with a fairly standard name: IMG_20180408_153807553_HDR.jpg

And yet it is not treated by the Imagor server with this error:
{"Op":"Get","URL":"https://pho/tst/IMG_20180408_153807553_HDR.jpg","Err":{"Op":"dial","Net":"tcp","Source":null,"Addr":null,"Err":{"Err":"no such host","Name":"pho","Server":"127.0.0.11:53","IsTimeout":false,"IsTemporary":false,"IsNotFound":true}}}

Other images with underscores or uppercase letters are being treated fine in the exact same environment.
The only difference is that the image weighs 3MB but that should not be an issue and is not what the error reports anyway.

Please let me know what I am doing wrong.

Add source whitelist

We have a large number of services and sharing the secret between the services has been proven difficult. As an alternative we are using ALLOWED_SOURCES in thumbor:

ALLOWED_SOURCES: >
            [".*\.foobar\.com","my\.foobar\.com", "sendbird-ap-9\.s3\.amazonaws\.com"]

This will only accept and process images from the listed sources.

Looking at something similar in imagor.

Support for path style S3 URL

Hi,
I am trying to use a S3 object storage which doesn't support subdomain style URL like bucket.namespace.domain.tld. It supports only path style URL like namespace.domain.tld/bucket. Can you please make changes to the s3storage adapter so that it could support path style URL too?
Maybe a new config option for s3 storage. Sometjing like S3_URL=path (default url) would be nice. I am not familiar with GO. So, I can't make a PR.

Thanks

Results not saved (imagor: 400 pass)

Hi,

I'm trying a basic setup to replace thumbor but I can't manage to make it save any results.
The configuration seems ok:

        - name: PORT
          value: "8080"
        - name: DEBUG
          value: "1"
        - name: IMAGOR_UNSAFE
          value: "0"
        - name: IMAGOR_AUTO_WEBP
          value: "1"
        - name: IMAGOR_SECRET
          value: "hzieQh8AJkwKaOx2Oj4ywYWYt9ScoS2mlM6sjLrpETVU5cdYNVXA"
        - name: FILE_STORAGE_BASE_DIR
          value: "/data/store"
        - name: FILE_STORAGE_PATH_PREFIX
          value: 'store'
        - name: FILE_RESULT_STORAGE_BASE_DIR
          value: "/data/store"
        - name: FILE_RESULT_STORAGE_PATH_PREFIX
          value: 'results'

But in the logs I see:

2022-04-13T10:45:01.424Z    WARN    imagor/imagor.go:355    save    {"key": "fit-in/1280x0/filters:format(webp)/https://some-domain.com/uploads/2022/04/some-file.png", "error": "imagor: 400 pass"}

Looking in the code, this error can happen (if I understand correctly) if the path doesn't begin with / or if it matches /.. The initial / is prepended by the file storage and I don't see any /. anywhere so I'm a bit stumped.

Crop params in percent size

Hi,
Thanks for this tool. It looks amazing !

My use case is to crop images by giving left-top and right-bottom coords as percent of width and height of the image.
So the params would be float values between 0 and 1, such as "0.10x0.08:0.26x0.43".

Of course, I don't have image width and height infos on the client side, otherwise it would have been easy to compute pixels-coords automatically.

Is there any way to achieve this with the current version of Imagor ? If not, would you be open to a PR ?

Disable default caching

Hi, I tried using this as a docker image but it has problem with caching.

I don't want the cache header because of CDN caching, but I could not disable it.

I'm using docker-compose with these variables but still getting cache header with default values
Cache-Control: public, s-maxage=86400, max-age=86400, no-transform

IMAGOR_CACHE_HEADER_NO_CACHE: 1
IMAGOR_CACHE_HEADER_TTL: 0h0m0s
IMAGOR_CACHE_HEADER_TTL: 0h0m0s

Please disable the cache for default.

Saved images are not treated

  • I am using version 0.2.4
  • When saving images, they are identical to the original and do not bear the treatments applied on the fly.

signature mismatch if `imagor-signer-truncate` not between 26 and 28

Version: 0.9.10

It appears that the path parser only detects hashes with a length between 26 and 30 characters as seen below:

"((unsafe/)|([A-Za-z0-9-_=]{26,30})/)?" +

Further, it checks if that segment is less or equal to 28 characters:

if match[index+1] == "unsafe/" {
p.Unsafe = true
} else if len(match[index+2]) <= 28 {
p.Hash = match[index+2]
}

Is this intended?

The example here sets imagor-signer-truncate to 32, but that will not work with the behavior above.

Loading an image directly from S3 Storage

Hello, first of all, thank you for the great tool!

I have a question and hope you could help me.

I have a minio server and two buckets in it, the first one is named source, the second one is result
I upload a random image into source bucket, and it is directly accessible via this link, for example http://localhost:9000/source/ship.jpg
So my question is, is it possible to pass only an image name (or its directory + image name) as an argument to imagor and the image will be fetched directly from S3 and after processing stored in result bucket?

For example, like this: http://localhost:8000/unsafe/200x200/ship.jpg

These are my docker env parameters

    environment:
      - 'PORT=8000'
      - 'AWS_REGION=us-east-1'
      - 'AWS_ACCESS_KEY_ID=***'
      - 'AWS_SECRET_ACCESS_KEY=***'
      - 'S3_ENDPOINT=http://minio2:9000/'
      - 'S3_STORAGE_BUCKET=source'
      - 'S3_RESULT_STORAGE_BUCKET=result'
      - 'SERVER_ACCESS_LOG=1'
      - 'IMAGOR_UNSAFE=1'
      - 'DEBUG=1'

Proxy for idempotent behaviour

With a proxy and TTL cache when we make the same request again it will use the cache.

is there anything like this currently in the code base ?

Image files that have a name containing "+" are not treated

With the same setting as #2 , I have tried to treat images from the map folder.
They are named according to another scheme, for example: map/air-lanes-jfk-lga-2018-5b+6DVXbdWRw86PJv+0.png

When I try to treat the above image I get an error:

  • the URL: http://mdi.lan.ourquietplaces.com:7123/unsafe/fit-in/-500x500/10x10/filters:hue(290):saturation(100):fill(yellow):format(jpeg):quality(80)/map/air-lanes-jfk-lga-2018-5b+6DVXbdWRw86PJv+0.png
  • the error I get: {"Op":"Get","URL":"https://'map/air-lanes-jfk-lga-2018-5b%206DVXbdWRw86PJv%200.png%27","Err":{"Op":"dial","Net":"tcp","Source":null,"Addr":null,"Err":{"Err":"no such host","Name":"'map","Server":"","IsTimeout":false,"IsTemporary":false,"IsNotFound":true}}}

best way to handle large gifs?

anyone have recommendations on how to handle large gifs. I find the processing time slow and i do notice that the resize makes the gifs have empty frames at the end...

animated gif support?

Hi again :)

Just wondering if there are any plans to support animated gifs? It seems like imagor returns a still image for gifs...

Access log

Hi,

How can I enable the HTTP access log for the imagor? If it is possible how can I change the log format?

Automatic webp conversion

If the browser has the capability the image should be returned in WebP, instead of JPEG.

thumbor related option: AUTO_WEBP: "True"

local minio server not support https protocol

{"level":"warn","ts":1647773368.5157983,"caller":"imagor/imagor.go:331","msg":"save","key":"fit-in/300x290/http://minio:9000/example-bucket/xxxximgpath.png","error":"RequestError: send request failed\ncaused by: Put \"https://minio:9000/example-bucket/path/rets/fit-in/300x290/http%253A/minio%253A9000/example-bucket/xxxximgpath.png\": http: server gave HTTP response to HTTPS client"}

Any advice on using imagor with lambda

Hi @cshum

Would you be open to accepting PRs that add lambda support?

Have you perhaps done any experimentation with imagor in a AWS lambda environment? ​It'd probably involve integrating https://github.com/aws/aws-lambda-go support. https://aws.amazon.com/solutions/implementations/serverless-image-handler/ / https://github.com/aws-solutions/serverless-image-handler also uses libvips, so it should be reasonably possible to do so. That tool, however, doesn't support smart resizing without additional AWS services.

Additionally:

One of our use cases is that we serve images at common breakpoints. We've found that pre-caching images at the breakpoints improves end-user performance. Given this, I was wondering if you'd be open to accepting PRs that also change the operating model a bit? In short: I'd like a lambda that accepts an SQS request in Thumbor parameter format, fetches the origin image, resizes it, and then stores the result in an S3 result cache. This way, when a web browser makes a request for the same file with the same breakpoint it is already pre-built and can be returned by the http version of imagor. Or, if it's a miss, it'll perform the resize.

Thanks!

Oskar

Resizing Not Working If Increasing Image Size W/O Stretch Param

As we are testing imagor as a true drop-in replacement for Thumbor, we have noticed that the basic resizing of an image does not work the same when trying to make an image larger.

For example, with Thumbor, if I want to make an image that is 640x480 increase to a width of 780, I can simply use these minimal URL parameters:

http://localhost:8000/unsafe/780x0/photos%2Fb%2Fef88c521cbbb57e05c12a2dcc696724f12c9155f.jpg

Using that same URL in imagor displays the image in it's original size.

I know that imagor does not have all of the features as Thumbor as of yet but wanted to point this out because it is a basic way to increase image sizes that I'm sure I'm not the only one doing this. Even though imagor offers a stretch param that works, to be a true drop in replacement, it would be nice to not have to use this as it may not be easy to change the url params in whatever backend code utilizes it.

Create Heroku support

Hi
I love your contribution, and it would be great if this tool could be easy to host on Heroku.
Thank you

同学,您这个项目引入了53个开源组件,存在1个漏洞,辛苦升级一下

检测到 cshum/imagor 一共引入了53个开源组件,存在1个漏洞

漏洞标题:Gin-Gonic Gin 环境问题漏洞
缺陷组件:github.com/gin-gonic/[email protected]
漏洞编号:CVE-2020-28483
漏洞描述:Gin-Gonic Gin是Gin-Gonic团队的一个基于Go语言的用于快速构建Web应用的框架。
github.com/gin-gonic/gin 全部版本存在安全漏洞,该漏洞源于可以通过设置X-Forwarded-For头来欺骗客户端的IP。
影响范围:(∞, 1.7.7)
最小修复版本:1.7.7
缺陷组件引入路径:github.com/cshum/imagor@->github.com/gin-gonic/[email protected]

另外还有几个漏洞,详细报告:https://mofeisec.com/jr?p=ab77bc

Typical memory usage for docker instance

We are running a single docker container with s3 loader and storage and memory usage hangs out around 2 Gib (last I checked 1.819GiB of total 7.667GiB). And it seems the memory grows and is not freed when an image is not found in the cache. Is that a reasonable/expected memory usage?

S3 paths with url encoding not working

I am currently testing locally imagor as a drop in replacement for our Thumbor implementation. We currently use an S3 loader and we're trying to emulate that with our local testing. However, we are getting errors when trying to implement it. I believe it has to do with url encoding special characters in the file path.

Here is the url we are using:

http://localhost:8888/unsafe/400x200/smart/photos/j/006b8bbe2c1baa8f2d12c06988a6efa7a15f048d.jpg

Here is the result:

{"message":"Get \"https://photos/j/006b8bbe2c1baa8f2d12c06988a6efa7a15f048d.jpg\": dial tcp: lookup photos on 127.0.0.11:53: no such host","status":500}

If I url encode the path of the photo, then it works:

photos%2Fj%2F006b8bbe2c1baa8f2d12c06988a6efa7a15f048d.jpg

But if the url encoded path has special characters in the filename, such as { to %7B, it gets an error:

photos%2F{1089856636549d026d369fb}.jpg

to

photos%2F%7B1089856636549d026d369fb%7D.jpg
{"message":"Get \"https://photos/%7B1089856636549d026d369fb%7D.jpg\": dial tcp: lookup photos on 127.0.0.11:53: no such host","status":500}

I'm assuming this is an issue because when querying s3 for the file, imagor is passing the url-encoded string instead of the actual filename. imagor should probably url-decode the string before making the request to s3. Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.