2020-01-19 09:55:18 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-07-21 19:40:39 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2020-01-19 09:55:18 +00:00
|
|
|
"errors"
|
2020-07-21 19:40:39 +00:00
|
|
|
"io"
|
2020-01-19 09:55:18 +00:00
|
|
|
"io/ioutil"
|
2020-07-21 19:40:39 +00:00
|
|
|
"net/http"
|
2020-01-19 09:55:18 +00:00
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ImageCompression interface {
|
|
|
|
Compress(url string) (location string, err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tinify
|
|
|
|
type Tinify struct {
|
|
|
|
// API Key
|
|
|
|
key string
|
|
|
|
}
|
|
|
|
|
2020-04-20 18:59:11 +00:00
|
|
|
func (t *Tinify) Compress(url string) (location string, err error) {
|
2020-01-19 09:55:18 +00:00
|
|
|
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
|
|
|
|
}
|
2020-07-21 19:40:39 +00:00
|
|
|
// Shrink
|
|
|
|
shrinkedUrl, e := t.shrink(url)
|
2020-01-19 09:55:18 +00:00
|
|
|
if e != nil {
|
|
|
|
err = errors.New("failed to compress file")
|
|
|
|
return
|
|
|
|
}
|
2020-07-21 19:40:39 +00:00
|
|
|
// Create temp file
|
2020-01-19 09:55:18 +00:00
|
|
|
file, e := ioutil.TempFile("", "tiny-*."+fileExtension)
|
|
|
|
if e != nil {
|
|
|
|
err = errors.New("failed to create temporary file")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer func() {
|
2020-07-21 19:40:39 +00:00
|
|
|
// Cleanup
|
2020-04-20 21:04:01 +00:00
|
|
|
_ = file.Close()
|
2020-01-19 09:55:18 +00:00
|
|
|
_ = os.Remove(file.Name())
|
|
|
|
}()
|
2020-07-21 19:40:39 +00:00
|
|
|
// Resize and download image
|
|
|
|
e = t.resize(shrinkedUrl, file)
|
2020-01-19 09:55:18 +00:00
|
|
|
if e != nil {
|
2020-07-21 19:40:39 +00:00
|
|
|
err = errors.New("failed to resize file or save compressed file")
|
2020-01-19 09:55:18 +00:00
|
|
|
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
|
|
|
|
}
|
2020-07-21 19:40:39 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|