package main import ( "bytes" "encoding/json" "errors" "io" "io/ioutil" "net/http" "os" "sort" "strings" ) type ImageCompression interface { Compress(url string) (location string, err error) } // Tinify type Tinify struct { // API Key key string } func (t *Tinify) Compress(url string) (location string, err error) { fileExtension := func() string { spliced := strings.Split(url, ".") return spliced[len(spliced)-1] }() supportedTypes := []string{"jpg", "jpeg", "png"} sort.Strings(supportedTypes) i := sort.SearchStrings(supportedTypes, strings.ToLower(fileExtension)) if !(i < len(supportedTypes) && supportedTypes[i] == strings.ToLower(fileExtension)) { err = errors.New("file not supported") return } // Shrink shrinkedUrl, e := t.shrink(url) if e != nil { err = errors.New("failed to compress file") return } // Create temp file file, e := ioutil.TempFile("", "tiny-*."+fileExtension) if e != nil { err = errors.New("failed to create temporary file") return } defer func() { // Cleanup _ = file.Close() _ = os.Remove(file.Name()) }() // Resize and download image e = t.resize(shrinkedUrl, file) if e != nil { err = errors.New("failed to resize file or save compressed file") return } hashFile, e := os.Open(file.Name()) defer func() { _ = hashFile.Close() }() if e != nil { err = errors.New("failed to open temporary file") return } fileName, err := getSHA256(hashFile) if err != nil { return } location, err = SelectedMediaStorage.Upload(fileName+"."+fileExtension, file) return } func (t *Tinify) shrink(source string) (string, error) { type Source struct { Url string `json:"url"` } data := struct { Source Source `json:"source"` }{ Source: Source{ Url: source, }, } body := new(bytes.Buffer) err := json.NewEncoder(body).Encode(data) if err != nil { return "", err } req, err := http.NewRequest("POST", "https://api.tinify.com/shrink", body) if err != nil { return "", err } req.SetBasicAuth("api", t.key) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return "", err } if resp.StatusCode != 201 { return "", errors.New("not 201 response code") } return resp.Header.Get("Location"), nil } func (t *Tinify) resize(output string, file *os.File) error { type Resize struct { Method string `json:"method"` Width int `json:"width"` } data := struct { Resize Resize `json:"resize"` }{ Resize: Resize{ Method: "scale", Width: 2000, }, } body := new(bytes.Buffer) err := json.NewEncoder(body).Encode(data) if err != nil { return err } req, err := http.NewRequest("POST", output, body) if err != nil { return err } req.SetBasicAuth("api", t.key) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return err } if resp.StatusCode != 200 { return errors.New("not 201 response code") } defer resp.Body.Close() _, err = io.Copy(file, resp.Body) return err }