-
Notifications
You must be signed in to change notification settings - Fork 0
/
imageoptim.go
128 lines (106 loc) · 2.85 KB
/
imageoptim.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package imageoptim
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
)
type Client struct {
apiUser string
}
type Option struct {
value string
}
// ImageOptim options. See https://imageoptim.com/api/post
var (
Full = Option{"full"}
Fit = Option{"fit"}
ScaleDown = Option{"scale-down"}
QualityLow = Option{"quality=low"}
)
func NewClient(apiUser string) *Client {
return &Client{apiUser}
}
func (c *Client) OptimizeImage(options []Option, input string) ([]byte, error) {
if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
requestURL := c.createURLForURL(options, input)
return c.processRequest(requestURL, nil, "")
}
return c.processLocalFile(options, input)
}
func (c *Client) createURLForURL(options []Option, imageURL string) string {
var optStrings []string
for _, opt := range options {
optStrings = append(optStrings, opt.value)
}
return fmt.Sprintf("https://im2.io/%s/%s/%s", c.apiUser, strings.Join(optStrings, ","), imageURL)
}
func (c *Client) processLocalFile(options []Option, imagePath string) ([]byte, error) {
requestURL := c.createURLForLocal(options)
var b bytes.Buffer
w := multipart.NewWriter(&b)
file, err := os.Open(imagePath)
if err != nil {
return nil, err
}
defer file.Close()
fw, err := w.CreateFormFile("file", filepath.Base(imagePath))
if err != nil {
return nil, err
}
if _, err = io.Copy(fw, file); err != nil {
return nil, err
}
w.Close()
return c.processRequest(requestURL, &b, w.FormDataContentType())
}
func (c *Client) createURLForLocal(options []Option) string {
var optStrings []string
for _, opt := range options {
optStrings = append(optStrings, opt.value)
}
return fmt.Sprintf("https://im2.io/%s/%s", c.apiUser, strings.Join(optStrings, ","))
}
func (c *Client) processRequest(requestURL string, body io.Reader, contentType string) ([]byte, error) {
req, err := http.NewRequest("POST", requestURL, body)
if err != nil {
return nil, err
}
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
bodyBytes, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("error response from ImageOptim: %s", string(bodyBytes))
}
if warning, ok := resp.Header["Warning"]; ok {
fmt.Println("Warning:", warning)
}
return io.ReadAll(resp.Body)
}
func WidthxHeight(width, height int) Option {
return Option{fmt.Sprintf("%dx%d", width, height)}
}
func Quality(preset string) Option {
return Option{fmt.Sprintf("quality=%s", preset)}
}
func Format(name string) Option {
n := strings.ToLower(name)
if n == "jpg" {
n = "jpeg"
}
return Option{fmt.Sprintf("format=%s", n)}
}
func Timeout(seconds int) Option {
return Option{fmt.Sprintf("timeout=%d", seconds)}
}