func NewImageProcessor(c <-chan processImageReq, name string) *imageProcessor {
	client, err := serviceaccount.NewClient(&serviceaccount.Options{
		Transport: &RetryTransport{http.DefaultTransport, 5},
	})
	if err != nil {
		log.Panicf("Failed to create service account client: %v\n", err)
	}
	service, err := storage.New(client)
	if err != nil {
		log.Panicf("Failed to create GCS client: %v\n", err)
	}
	return &imageProcessor{
		c:      c,
		client: client,
		s:      service,
		l:      log.New(os.Stderr, name, log.LstdFlags),
	}
}
NewImageProcessor constructs an imageProcessor which listens for input on the provided channel
and logs to stderr with its name as the prefix.
package main
import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"
	"code.google.com/p/goauth2/compute/serviceaccount"
	storage "code.google.com/p/google-api-go-client/storage/v1"
)
const (
	NumImageProcessors    = 2
	ImageProcessQueueSize = 50
	ThumbnailSuffix       = "-t"
)
var (
	hostname string
	retryableCodes = map[int]bool{
		http.StatusForbidden:           true,
		http.StatusInternalServerError: true,
		http.StatusBadGateway:          true,
		http.StatusServiceUnavailable:  true,
		http.StatusGatewayTimeout:      true,
	}
)
type RetryTransport struct {
	http.RoundTripper
	maxTries int
}
func (t *RetryTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	var body []byte
	if req.Body != nil {
		body, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return
		}
	}
	for i := 0; i < t.maxTries; i++ {
		if i != 0 {
			req = copyRequest(req, body)
		}
		resp, err = t.RoundTripper.RoundTrip(req)
		if err == nil {
			if !retryableCodes[resp.StatusCode] {
				break  
			}
			resp.Body.Close()
		}
	}
	return
}
func copyRequest(req *http.Request, body []byte) *http.Request {
	nreq, err := http.NewRequest(req.Method, req.URL.String(), bytes.NewReader(body))
	if err != nil {
		log.Panicf("Unable to copy http request: %v", err)
	}
	for k, vv := range req.Header {
		vv2 := make([]string, len(vv))
		copy(vv2, vv)
		nreq.Header[k] = vv2
	}
	return nreq
}
type processImageReq struct {
	sourceBucket, filename, saveToBucket, saveToFilename string
}
func getProcessImageReq(r *http.Request) (processImageReq, error) {
	objectPath := r.FormValue("id")
	if objectPath == "" {
		return processImageReq{}, errors.New("Request did not provide image id.")
	}
	sourceBucket, filename := path.Split(objectPath)
	sourceBucket = strings.Trim(sourceBucket, "/")
	saveToBucket := r.FormValue("save-to")
	extension := filepath.Ext(filename)
	name := filename[:len(filename)-len(extension)]
	filenameElements := []string{name, ThumbnailSuffix, extension}
	saveToFilename := strings.Join(filenameElements, "")
	return processImageReq{
		sourceBucket:   sourceBucket,
		filename:       filename,
		saveToBucket:   saveToBucket,
		saveToFilename: saveToFilename,
	}, nil
}
type imagemagickHandler struct {
	c chan<- processImageReq
}
func (h *imagemagickHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	req, err := getProcessImageReq(r)
	if err != nil {
		w.WriteHeader(500)
		return
	}
	select {
	case h.c <- req:
		fmt.Fprintf(w, "hostname=%s", hostname)
	default:
		w.WriteHeader(503)
	}
}
func NewImagemagickHandler(queueSize, numRoutines int) (h *imagemagickHandler) {
	c := make(chan processImageReq, queueSize)
	h = &imagemagickHandler{c: c}
	for i := 0; i < numRoutines; i++ {
		p := NewImageProcessor(c, fmt.Sprintf("Processor(%d)", i))
		go p.process()
	}
	return
}
type imageProcessor struct {
	c      <-chan processImageReq
	client *http.Client
	s      *storage.Service
	l      *log.Logger
}
func (p *imageProcessor) process() {
	for r := range p.c {
		t := time.Now()
		if err := p.processImage(r); err != nil {
			p.l.Printf("Could not process image %v: %v\n", r.saveToFilename, err)
		}
		p.l.Printf("Processing took %fs\n", time.Since(t).Seconds())
	}
}
func (p *imageProcessor) getImageBytes(sourceBucket, filename string) (b []byte) {
	obj, err := p.s.Objects.Get(sourceBucket, filename).Do()
	if err != nil {
		p.l.Panicf("Unable to get object %v from GCS: %v\n", filename, err)
	}
	resp, err := p.client.Get(obj.MediaLink)
	if err != nil {
		p.l.Panicf("Unable to download %v: %v\n", obj.MediaLink, err)
	}
	defer resp.Body.Close()
	b, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		p.l.Panicf("Unable to read body of %v: %v\n", obj.MediaLink, err)
	}
	return
}
func thumbnailCommand(in, out string) *exec.Cmd {
	return exec.Command("convert", in, "-thumbnail", "100x100", out)
}
func intenseCommand(in, out string) *exec.Cmd {
	return exec.Command("convert", in, "-auto-level", "-auto-orient", "-antialias",
		"-auto-gamma", "-contrast", "-despeckle", "-thumbnail", "100x100", out)
}
func moderateCommand(in, out string) *exec.Cmd {
	return exec.Command("convert", in, "-auto-orient", "-antialias", "-contrast", "-thumbnail",
		"100x100", out)
}
func (p *imageProcessor) processImage(r processImageReq) (err error) {
	b := p.getImageBytes(r.sourceBucket, r.filename)
	p.l.Printf("Read %d bytes from response body...\n", len(b))
	if err = ioutil.WriteFile(r.filename, b, 0600); err != nil {
		p.l.Printf("Error writing file %v to disk\n", r.filename)
		return
	}
	defer os.Remove(r.filename)  
	cmd := moderateCommand(r.filename, r.saveToFilename)
	out, err := cmd.CombinedOutput()
	if err != nil {
		p.l.Printf("Could not transform file. StdOut: %v\n", string(out))
		return
	} else {
		p.l.Printf("Converted %s to %s\n", r.filename, r.saveToFilename)
	}
	defer os.Remove(r.saveToFilename)  
	p.l.Println("Now starting upload to save-to Cloud Storage Bucket...")
	object := &storage.Object{Name: r.saveToFilename}
	file, err := os.Open(r.saveToFilename)
	if err != nil {
		p.l.Printf("Error opening %q\n", r.saveToFilename)
		return
	}
	defer file.Close()
	res, err := p.s.Objects.Insert(r.saveToBucket, object).Media(file).Do()
	if err != nil {
		p.l.Printf("Unable to upload %v to GCS\n", r.saveToFilename)
	}
	p.l.Printf("Created object %v at location %v\n", res.Name, res.SelfLink)
	return
}