Coder Social home page Coder Social logo

vip's Introduction

VOKAL Image Proxy

line cvr Build Status

vip is an image proxy designed for easily resizing and caching images.

Usage

Getting resized images

Images are served up with a URI that contains a bucket name, as well as a unique identifier for the image: http://images.example.com/mybucket/5272a0e7d0d9813e21.

When images are requested they are placed into an in-memory cache to make repeated requests for that image faster.

You can resize an image on the fly by providing an ?s=X parameter (where X is an integer) that specifies a maximum width for the image in pixels. Resized images may also be center-cropped by passing ?c=true in the querystring. The resized image or thumbnail will then be cached to both groupcache and S3. If the image leaves the in-memory cache it will not need to be resized again.

For example:

  • If you want to resize an image down to a 500 pixel size:
    http://images.example.com/mybucket/5272a0e7d0d9813e21?s=500
  • If you needed a square thumbnail of the same image:
    images.example.com/mybucket/5272a0e7d0d9813e21?s=160&c=true.

For performance reasons, vip has a configurable maximum width, set via the environment variable VIP_MAX_WIDTH. You'll want to balance your own app's needs with memory needed to cache larger images, though the default max is a reasonable 720 pixels.

Uploading images

Images are uploaded through vip to generate the serving URL. Upload requests should have the raw binary data of the image encoded as the body. Content-Type and authentication headers will also need to be provided. The route for uploads is: images.example.com/upload/mybucket.

This will upload the supplied image into an S3 bucket named mybucket and return a JSON-encoded serving URL:

{
    "url": "http://images.example.com/mybucket/5272a0e7d0d9813e21"
}

You can also limit the maximum filesize that vip can accept by specifying VIP_SIZE_LIMIT in megabytes (e.g. VIP_SIZE_LIMIT=10). The default is 5MB, which is generally sufficient for JPEG photos from most mobile devices.

Pre-warming the cache

For mobile clients that use one or more common sizes, those sizes can be cached in the background while uploading a new image. Simply set a comma-delimited list of query parameters for each expected size in a X-Vip-Warmup header:

X-Vip-Warmup: s=250&c=true,s=500,s=1024

This will automatically generate a 250x250px thumbnail, as well as 500px wide and 1024px wide versions of the uploaded image, and all three will be ready in the cache for immediate retreival.

Deployment

vip can be deployed with Docker:

    $ docker run -e AWS_SECRET_ACCESS_KEY=... \
        -e AWS_ACCESS_KEY_ID=... vokal/vip

It is recommended that you deploy vip behind SSL and with an authentication token. This token can be generated by your own application, but should be used by clients when uploading.

Authentication is only checked during uploads. The expected format for authentication is in the X-Vip-Token header:

X-Vip-Token: c5411c3aac6f2c6d55a1fdc2d0a98c49

To set the token for your deployment, pass it as the environment variable AUTH_TOKEN when deploying:

$ docker run -e AUTH_TOKEN=... -e AWS_SECRET_ACCESS_KEY=... \
    -e AWS_ACCESS_KEY_ID=... vokalinteractive/vip

To configure CORS support for browser-based clients, supply a comma separated list (no spaces) of allowed hosts in the environment variable ALLOWED_ORIGIN. Setting ALLOWED_ORIGN=* allows any host; setting ALLOWED_ORIGIN=*.project.com allows any subdomain of project.com. For staging setups, you likely want to allow localhost as well, or any upload tests from drone or a local dev environment will fail. Ex: ALLOWED_ORIGIN=localhost,*.project.com. (Note: Requests from an allowed origin do not require an X-Vip-Token.)

Configuration Summary

  • AWS_ACCESS_KEY: The access key for an IAM user or role with access to S3 bucket(s)
  • AWS_SECRET_ACCESS_KEY: The secret key for an IAM user or role with access to S3 bucket(s)
  • AWS_REGION: The AWS region in which image storage buckets are configured (default us-east-1)
  • URI_HOSTNAME: The hostname used to build image URLs, e.g. images.example.com
  • AUTH_TOKEN: A secret token that non-browser clients can use to autheticate for uploads. If no token is supplied, anyone could upload to your image proxy
  • ALLOWED_ORIGIN: a comma-delimited list of hostnames to accept CORS requests from browser-based clients, e.g. www.example.com,*.example2.com will accept uplaod requests from pages originating from www.example.com or any subdomain of example2.com. If this is not set, CORS is disabled and will likely fail for any upload requests from a browser.
  • VIP_SIZE_LIMIT: A maximum file-size limit in megabytes (default 5)
  • VIP_MAX_WIDTH: A maximum width for resized images in pixels (default 720)

For serving via HTTPS (recommended), vip expects to find an SSL certificate as well as the matching private key in the following locations:

  • /etc/vip/application.pem
  • /etc/vip/application.key

By default, vip will also respond to HTTP/2 requests; however, some mobile clients have incomplete implementations and may fail. You can turn off HTTP/2 support by setting DISABLE_HTTP2 to True. In particular, iOS 8 clients have problems with this draft version of HTTP/2.

Cloudfront

vip can (and should be) used behind a CDN like Amazon Cloudfront. To use vip behind the Cloudfront CDN setup a custom origin with the FQDN of your proxy, and also permit POST HTTP methods on the distribution.

alt text

Maintainer

Chris Foresman (foresmac) [email protected]

vip's People

Contributors

scottferg avatar foresmac avatar workhorseindustries avatar jweber13 avatar jrit avatar adambain-vokal avatar

Stargazers

 avatar Boris V avatar

Watchers

 avatar Bill Best avatar James Cloos avatar  avatar  avatar TJ Soptame avatar  avatar  avatar Jose Garcia avatar Steve J. avatar  avatar

vip's Issues

Use OpenCV for image manipulation

I've been tossing around this idea for probably a year and tabled it because I didn't like the idea of installing so many unpredictable dependencies on a production machine. Fortunately, Docker fixes this. This would effectively replace the use of the current image resizing library and would require a new in-house library built on top of the OpenCV C-bindings. This guy has already started on that path: https://github.com/daddye/trez

Add environment variables to the README

Currently only some of the available environment variables are documented, but things like URI_HOSTNAME and DISABLE_HTTPS2 and AWS_REGION.

Additionally, the locations for the cert/key is undocumented

Rotation is still wonky

Images from the iPhone 6 have been correctly rotated, but we just discovered that those from an iPhone 5 are not. Are there multiple standards for this kind of thing? What a pain in the ass.

The ones we've seen problems with were taken with the rear camera on iPhone 5, in various orientations

This one was taken sideways and worked correctly, but the other three orientations did not:

I need to do another round of testing with the two camera to give you a full sample set.

@foresmac

[suggestion] smart crop

Was just looking at this lib for JS https://github.com/jwagner/smartcrop.js/ which crops based on some properties of the image to try and create a better focal area. I'm not sure if there is anything similar for Go or if someone wants to try porting this. Seems like it would be a good add though.

Support warm-up requests

This could likely be done through a custom X-Vip-Warmup header that provides several sizes (remember that HTTP headers are lists by default).

Resizing is slow, ther'es no avoiding that even with fast resizing. Warm up requests would be a way to mitigate that speed concern by triggering jobs to resize an image well before it's requested.

I've said it before, I'll repeat it here, I would love to see a Go library for building a concurrent worker queue. This is a great case. Y goroutines per-Vip instance are running to handle warmup requests and they get queued up automatically. This fits a ton of workflows and it's not difficult to design and build.

Clustering (and maybe config) should be coordinated with etcd

@foresmac large change here, but one that would be valuable to use on other projects as well. etcd (https://github.com/coreos/etcd) has a very nice Go client that would work well in Vip. Currently all Vip instances are running as naive instances that are unaware of other groupcache peers. Theoretically a Vip instance should be able to come online, ping etcd for other Vip instances, and establish connections to build out the p2p network. This is necessary for Vip clusters to grow organically. The old method of querying for EC2 tags was very hamfisted and worked off and on.

Additionally, any Vip configuration could also live in Etcd (since that's what it's for). That would simplify deployments immensely.

Resizing an image strips EXIF data

HTTP2 support

This is experimental but being worked on by the net/http maintainer and he's using in production in a number of places. Looks like a simple call to http2.ConfigureServer() is all that is necessary. This would be enormously beneficial for clients that are loading multiple images at once.

https://godoc.org/github.com/bradfitz/http2

VIP from chrome extension for vokal talk

Domain auth won't work because it isn't really running from a domain.

Token auth with a built-in token is potentially an option, but #40 needs to be fixed. I'm concerned that someone can just unpack the extension and use the token though - not so much in this use case as in general.

I was also just discussing with @projectweekend that zuul could provide a jwt to the client after completing auth and vip would only need to know the same secret key as zuul to successfully decode the token and determine it is valid, which at least keeps vip from needing to keep track of tokens in a database and potentially provides a general way to handle auth at a user level instead of at a domain level.

Include image dimensions in the filename

I just discussed this with @scottferg: in some apps, it's helpful to know the aspect ratio of an image before downloading it, so the UI layout can be figured without having to wait for large images to download first. We eventually settled on a simple solution: include the original dimensions of the image in the random filename that's generated. So, for an image that's 1242 wide and 2208 tall, the filename might be ef13692177557cb6bbfd34241e0c6ad2-1242x2208. Since the app knows the URL to request for the photo, it can pull the dimensions out of that URL to determine how big the image will be once resized to the desired width.

Center crop even when resize isn't needed

Right now, with a request like s=200&c=true if the image is 180x90 there is no transform. I think it would be better if even when scaling isn't needed, if center-crop is true, that the cropping is just performed at the full image size. Right now it causes layout issues where square images are expected and they may not be just because the file dimensions are small.

@scottferg @foresmac agree?

See about using `vips` for image rotation.

Even if we don't enable libexif, changing the existing rotation code should also see performance improvements.

Need to write benchmarks for GetRotatedImage() to analyze before/after numbers.

Experiment with Quality setting in `vips` to balance JPEG quality vs image size

I don't this has a major performance impact, and I don't believe this option has any effect on PNG or other lossless formats. But on lossy formats, it should reduce file size considerably, which impacts network performance.

Right now this is hard-coded to 95; 80 is usually my go-to quality setting in Photoshop when balancing image size and quality. I tend to see no visible artifacts at this setting, though implementations can and do vary. Worth finding a couple sample images, running them through vips.Resize() at different settings, and comparing file size savings vs image quality.

Possible memory use optimization

@wmbest2 suggested a possible path to look into for memory optimization when uploading a file and dealing with decoding the EXIF data as well as the image data.

The idea is to read a small subset of the beginning of the file to send to exif.decode and deal with that in a go routing while reading the whole file into image.Decode.

Here's some pseudo code he came up with:

buf := make([]buf, 0, 4096)
buffer := bytes.NewBuffer(buf)

data := bytes.TeeReader(src, buffer)

go needsRotation(myChan, buffer)

image, format, err := image.Decode(data)

needs <- myChan 

Add support for WebP format images

Here is some helpful research:

  • image only contains a WebP decoder, not an encoder
  • Someone built an external library for encoding (and decoding) WebP that uses cgo with what I believe is libwebp. It might be "fun" to translate libwebp from C to Go as an open source project.
  • Currently only Chrome or Opera support viewing WebP images. We could, like Facebook, YouTube, and Netflix, serve WebP to browsers that send the proper accept header. However, users trying to save the images are frustrated when they can't do anything with them.
  • In general, WebP uses less data to provide a roughly comparable quality image (Using VP8 keyframe compression) in a format that as optimized for transfer over HTTP (e.g., file size is read from the first 8 bytes, image size within the first 24).
  • Container spec here
  • WebP API docs here

Allow bigger images

From the README:

The maximum width that can be provided is 720 pixels.

The iPhone 6 Plus is 1242 pixels wide, and it stands to reason that some apps will need to display images at full-width on the screen.

Add config switch for resize method

vips.BILINEAR is the default for performance. Add an option to set it to vips.BICUBIC for better image quality. Also, analyze performance difference between the two methods and document that so users can make an informed choice between the two options.

Supporting PDF files

Basically there is no Go library to render an PDF into a bitmap. Right now the best solution is Datalogics web-based API: https://api.datalogics-cloud.com/docs#renderpages

However, the developer of gofpdf is considering making a Go wrapper around Google's PDFium (used by Chrome to render PDFs in the browser, for instance). That is an open source implementation of Foxit's FPDF SDK in C++; which confusingly isn't the same as the open source FPDF library to generate PDF files; it cannot render or display them. He will alert us if he starts down that path. If we decide to do this on our own, I suggest open sourcing the blimp rendering bit and alerting him in kind. He may include the functionality as part of gofdpdf.

video with elastic transcode on media-proxy

@foresmac is out today, so I'm opening this issue to get my thoughts down after looking through the existing code on media-proxy.

Currently working, is a video is posted to vip and that is correctly saved to an S3 bucket. There is also currently an elastic transcoder workflow configured to take files from an input bucket and deposit a preview thumbnail and the transcoded file in the same bucket used to store the original uploaded file.

The way I believe this should work from here is:

  • vip should have a method to transcode video that can accept a Reader, so that the transcode can be completed from an existing or new file
  • That method should put the file to the transcoder input bucket, but not the standard bucket. We don't want to have to put a 100MB video file twice.
  • There should be an SNS HTTP completion message that is a hook back to the API, which can notify when the transcode is complete so the URL can be stored/updated. That URL is configured in SNS, so vip doesn't have to care about it, it will just hit the API.
  • vip should immediately return to the client the S3 URL for the original media but include a field indicating that transcode is pending. This original URL will point to the transcoder input bucket.
  • The API will have to match up transcode callbacks with posted media, which may sometimes mean that the completion callback is first, and other times mean that the the item is posted first. This can be a very simple lookup table, here is what transcode sends on completion: http://docs.aws.amazon.com/elastictranscoder/latest/developerguide/notifications.html

I can't think of a simpler way to do this that would be reliable, but please point out flaws you see. Looks like one new endpoint on the API, and very minor changes to vip to support this. This assumes I understand how Elastic Transcoder works, but it looks like a very simple task-based setup.

@scottferg care to comment?

Rotate image before determining dimensions

I just talked to @foresmac about #46. The dimensions in the file name (added in #54) are calculated before looking at the rotation information in the EXIF data. This means that the dimensions could appear as 100x200 or 200x100, depending on whether the image has rotation data. That's a problem on the client end, because the idea was to get the image dimensions without having to download the entire image, but under the current circumstances, that's what it would need to do: the dimension is in the filename, but the file has to be downloaded to check the EXIF data and determine whether those numbers need to be swapped.

Chris said he could rotate the image on upload and just let everything after that assume that it's right-side-up, but it sounds like that still needs to be explored.

Clean up docs

  • Separate out all config/deployment info from existing usage info
  • Add sample curl command
  • Add info about contributing
  • Document testing and benchmarking

extend to video and audio using Elastic Transcoder, call it Vokal Media Proxy?

Just an idea. Not sure if this is the right solution, but wanted to get thoughts about extending this to support either generic uploads or at least common media types and support building preview images for these file types. It would be a lot of development, but might be useful if done by extending the existing architecture.

Alternatively, maybe a solution is building something off of vip into VMP, and they could be separate installable options. Just kicking around ideas and posting this here if it seems useful across multiple projects.

Provide a static preview for GIFs

Says @jrit:

it looks like it is possible to get the image out of the gif with this http://golang.org/pkg/image/gif/#Encode which seems one step short of crop/resize

Should be possible to at least create a static JPEG (or possibly GIF) preview of static or animated GIFs.

Alternately, would be nice if we could just scale animated GIFs à la giphy.com.

groupcache should work in a more friendly fashion

Groupcache is kind of the bread and butter in Vip but it hasn't gotten much love. It's a pain in the ass to currently figure out other peers so that groupcache can dial them. This should be revisited and tests should be updated to cover it.

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.