@@ -5,22 +5,24 @@ import (
5
5
"bytes"
6
6
"encoding/json"
7
7
"errors"
8
+ "fmt"
9
+ "image"
10
+ "image/jpeg"
11
+ "image/png"
8
12
"io"
9
13
"io/ioutil"
10
14
"mime/multipart"
11
15
"net/http"
12
- "net/url"
16
+ u "net/url"
13
17
"os"
18
+ "path/filepath"
14
19
"strconv"
20
+ "strings"
15
21
16
22
"github.com/kennygrant/sanitize"
23
+ pngquant "github.com/manhtai/gopngquant"
17
24
)
18
25
19
- type File struct {
20
- FileName string `json:"FileName"`
21
- URL string `json:"URL"`
22
- }
23
-
24
26
func FromRequestToFile (req * http.Request , path string ) (string , string , error ) {
25
27
req .ParseMultipartForm (32 )
26
28
file , handler , err := req .FormFile ("file" )
@@ -96,6 +98,11 @@ type operations struct {
96
98
Ops []* operation
97
99
}
98
100
101
+ func NewProcessingOps () * operations {
102
+ ops := & operations {}
103
+ return ops
104
+ }
105
+
99
106
func (o * operation ) addParam (key string , val interface {}) {
100
107
if o .Params == nil {
101
108
o .Params = make (map [string ]interface {})
@@ -116,6 +123,7 @@ func (o *operations) last() *operation {
116
123
return o .Ops [len (o .Ops )- 1 ]
117
124
}
118
125
126
+ // ProcessedImage deprecated not flexible enough
119
127
func ProcessedImageFromRequest (req * http.Request , imageType string , width int , height int , quality int , convert bool ) ([]byte , error ) {
120
128
err := req .ParseMultipartForm (32 )
121
129
if err != nil {
@@ -129,6 +137,7 @@ func ProcessedImageFromRequest(req *http.Request, imageType string, width int, h
129
137
return ProcessedImage (file , imageType , width , height , quality , convert )
130
138
}
131
139
140
+ // ProcessedImage deprecated not flexible enough
132
141
func ProcessedImage (r io.Reader , imageType string , width int , height int , quality int , convert bool ) ([]byte , error ) {
133
142
ops := & operations {}
134
143
@@ -156,7 +165,12 @@ func ProcessedImage(r io.Reader, imageType string, width int, height int, qualit
156
165
if err != nil {
157
166
return nil , err
158
167
}
159
- endpoint := os .Getenv ("IMAGE_PROCESSING_ENDPOINT" ) + "pipeline?operations=" + url .QueryEscape (string (bOps ))
168
+
169
+ imgProcessingEndpoint := os .Getenv ("IMAGE_PROCESSING_ENDPOINT" )
170
+ if imgProcessingEndpoint == "" {
171
+ imgProcessingEndpoint = os .Getenv ("IMAGINARY_ENDPOINT" )
172
+ }
173
+ endpoint := imgProcessingEndpoint + "pipeline?operations=" + u .QueryEscape (string (bOps ))
160
174
161
175
var b bytes.Buffer
162
176
w := multipart .NewWriter (& b )
@@ -242,3 +256,86 @@ func DownloadToFileNoSanitize(url string, filename string, filepath string) (str
242
256
}
243
257
return FromBytesNoSanitize (filename , filepath , b )
244
258
}
259
+
260
+ type FileSaver interface {
261
+ SaveFile (filename string , b io.Reader ) (bts * bytes.Buffer , fileName string , url string , err error )
262
+ }
263
+
264
+ type imageProcessHelper struct {
265
+ fileSaver FileSaver
266
+ endpoint string
267
+ }
268
+
269
+ func NewImageHelper (endpoint string , fs FileSaver ) * imageProcessHelper {
270
+ return & imageProcessHelper {
271
+ fs ,
272
+ endpoint ,
273
+ }
274
+ }
275
+
276
+ func (ip * imageProcessHelper ) GetDimensions (bts []byte , ext string ) (width int , height int , err error ) {
277
+ ext = strings .ToLower (ext )
278
+ var imgConfig image.Config
279
+ if strings .HasSuffix (ext , "jpeg" ) || strings .HasSuffix (ext , "jpg" ) {
280
+ imgConfig , err = jpeg .DecodeConfig (bytes .NewBuffer (bts ))
281
+ } else {
282
+ imgConfig , err = png .DecodeConfig (bytes .NewBuffer (bts ))
283
+ }
284
+ if err != nil {
285
+ return - 1 , - 1 , fmt .Errorf ("failed to get the original image dimensions %v" , err )
286
+ }
287
+ return imgConfig .Width , imgConfig .Width , nil
288
+ }
289
+
290
+ func (ip * imageProcessHelper ) ProcessImage (filename string , bts []byte , ops * operations ) (byts * bytes.Buffer , fileName string , url string , err error ) {
291
+ ext := filepath .Ext (filename )
292
+
293
+ bOps , err := json .Marshal (ops .Ops )
294
+ if err != nil {
295
+ return nil , "" , "" , fmt .Errorf ("json.Marshal %v" , err )
296
+ }
297
+ endpoint := ip .endpoint + "pipeline?operations=" + u .QueryEscape (string (bOps ))
298
+ var b bytes.Buffer
299
+ mpW := multipart .NewWriter (& b )
300
+ fw , err := mpW .CreateFormFile ("file" , "placeholder." + ext )
301
+ if err != nil {
302
+ return nil , "" , "" , fmt .Errorf ("mpW.CreateFormFile %v" , err )
303
+ // ctx.ErrorJSON(http.StatusOK, "couldn't create form file ", err)
304
+ }
305
+ _ , err = io .Copy (fw , bytes .NewBuffer (bts ))
306
+ if err != nil {
307
+ // ctx.ErrorJSON(http.StatusOK, "failed to copy from reqFile", err)
308
+ return nil , "" , "" , fmt .Errorf ("failed to copy to multipart writer %v" , err )
309
+ }
310
+ err = mpW .Close ()
311
+ if err != nil {
312
+ return nil , "" , "" , fmt .Errorf ("failed to close multipart writer %v" , err )
313
+ }
314
+
315
+ req , err := http .NewRequest ("POST" , endpoint , & b )
316
+ if err != nil {
317
+ return nil , "" , "" , fmt .Errorf ("failed to copy from req %v" , err )
318
+ }
319
+ req .Header .Set ("Content-Type" , mpW .FormDataContentType ())
320
+
321
+ client := & http.Client {}
322
+ res , err := client .Do (req )
323
+ if err != nil {
324
+ return nil , "" , "" , fmt .Errorf ("statuscode %v" , err )
325
+ }
326
+ if res .StatusCode != 200 {
327
+ return nil , "" , "" , fmt .Errorf ("error status code is: %d" , res .StatusCode )
328
+ }
329
+ defer res .Body .Close ()
330
+
331
+ if ext == "png" {
332
+ cmpressedPNG := make ([]byte , 0 )
333
+ buf := bytes .NewBuffer (cmpressedPNG )
334
+ err = pngquant .CompressPng (res .Body , buf , 5 )
335
+ if err != nil {
336
+ return nil , "" , "" , fmt .Errorf ("pngquant.CompressPng %v" , err )
337
+ }
338
+ return ip .fileSaver .SaveFile (filename , buf )
339
+ }
340
+ return ip .fileSaver .SaveFile (filename , res .Body )
341
+ }
0 commit comments