Skip to content

Commit 21cc6c8

Browse files
author
Josh Cave
committed
simplifed image processing and created a filesaver interface to allow plugable saving of files. i.e. filesystem,gcloud,s3,etc
1 parent 6255bb4 commit 21cc6c8

File tree

3 files changed

+112
-7
lines changed

3 files changed

+112
-7
lines changed

fileupload.go

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,24 @@ import (
55
"bytes"
66
"encoding/json"
77
"errors"
8+
"fmt"
9+
"image"
10+
"image/jpeg"
11+
"image/png"
812
"io"
913
"io/ioutil"
1014
"mime/multipart"
1115
"net/http"
12-
"net/url"
16+
u "net/url"
1317
"os"
18+
"path/filepath"
1419
"strconv"
20+
"strings"
1521

1622
"github.com/kennygrant/sanitize"
23+
pngquant "github.com/manhtai/gopngquant"
1724
)
1825

19-
type File struct {
20-
FileName string `json:"FileName"`
21-
URL string `json:"URL"`
22-
}
23-
2426
func FromRequestToFile(req *http.Request, path string) (string, string, error) {
2527
req.ParseMultipartForm(32)
2628
file, handler, err := req.FormFile("file")
@@ -96,6 +98,11 @@ type operations struct {
9698
Ops []*operation
9799
}
98100

101+
func NewProcessingOps() *operations {
102+
ops := &operations{}
103+
return ops
104+
}
105+
99106
func (o *operation) addParam(key string, val interface{}) {
100107
if o.Params == nil {
101108
o.Params = make(map[string]interface{})
@@ -116,6 +123,7 @@ func (o *operations) last() *operation {
116123
return o.Ops[len(o.Ops)-1]
117124
}
118125

126+
// ProcessedImage deprecated not flexible enough
119127
func ProcessedImageFromRequest(req *http.Request, imageType string, width int, height int, quality int, convert bool) ([]byte, error) {
120128
err := req.ParseMultipartForm(32)
121129
if err != nil {
@@ -129,6 +137,7 @@ func ProcessedImageFromRequest(req *http.Request, imageType string, width int, h
129137
return ProcessedImage(file, imageType, width, height, quality, convert)
130138
}
131139

140+
// ProcessedImage deprecated not flexible enough
132141
func ProcessedImage(r io.Reader, imageType string, width int, height int, quality int, convert bool) ([]byte, error) {
133142
ops := &operations{}
134143

@@ -156,7 +165,12 @@ func ProcessedImage(r io.Reader, imageType string, width int, height int, qualit
156165
if err != nil {
157166
return nil, err
158167
}
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))
160174

161175
var b bytes.Buffer
162176
w := multipart.NewWriter(&b)
@@ -242,3 +256,86 @@ func DownloadToFileNoSanitize(url string, filename string, filepath string) (str
242256
}
243257
return FromBytesNoSanitize(filename, filepath, b)
244258
}
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+
}

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ module github.com/nerdynz/fileupload
22

33
go 1.12
44

5+
replace bitbucket.org/iqhive/tkt-shared => ../tkt-shared
6+
57
require (
68
github.com/kennygrant/sanitize v1.2.4
9+
github.com/manhtai/gopngquant v0.0.0-20180310144719-578cebc80102
10+
github.com/manhtai/imagequant v0.0.0-20180310104925-a76d35dcb570 // indirect
711
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd // indirect
812
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
22
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
3+
github.com/manhtai/gopngquant v0.0.0-20180310144719-578cebc80102 h1:bShgP1wiKAQf05xCYAVZJ0ghMq6vnBy+ioKGPip1gEI=
4+
github.com/manhtai/gopngquant v0.0.0-20180310144719-578cebc80102/go.mod h1:jHu+AhKQgxLdKw7pisWUAD+BeH38FhmHDTtDBsXG1sA=
5+
github.com/manhtai/imagequant v0.0.0-20180310104925-a76d35dcb570 h1:RJ95GXJWSewO5Mhi+jD4sX6b5RUk3S3/OiDO0+BBxCI=
6+
github.com/manhtai/imagequant v0.0.0-20180310104925-a76d35dcb570/go.mod h1:LTQQU89K3IGCYY8a1nqBJVT1rH29s4nBygn/653/HBk=
37
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
48
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
59
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=

0 commit comments

Comments
 (0)