-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
171 lines (137 loc) · 3.81 KB
/
main.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
This app is designed to serve compressed image assets.
Given a path such as `/uploads/cat.jpg?s=200` the app will check on
disk for a file with the name `cat_s200.jpg`. If this doesn't exist
we check for `cat.jpg` and compress it down to an image of size
200x200 and store this on disk before serving it.
If the `s` paramater is omitted the image will be served at its
natural size.
AllowedFileTypes defines what files the app will work with.
AllowedSizes defines which sizes the app will serve.
UploadsPath defined where to look for files.
TODO: Move config to command line args.
*/
package main
import (
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/nfnt/resize"
)
// debug tells the app to output useful dlogs
const debug bool = false
// AllowedFileTypes defines what files the app will work with.
var AllowedFileTypes = map[string]bool{
"jpg": true,
"jpeg": true,
"png": true,
}
// AllowedSizes defines which sizes the app will serve.
var AllowedSizes = map[int]bool{
100: true,
200: true,
600: true,
}
// UploadsPath defined where to look for files.
const UploadsPath string = "./uploads/"
// Output a message to stdout, but only if debug is true
func dlog(msg string) {
if debug {
fmt.Printf("%s\n", msg)
}
}
/*
Resize a file and save the result to disk. Maintains
aspect ratio when resizing.
source string The existing file path
target string The file path to write the output to
size int The max width of the output
string The output path
err An error which occurred when processing
*/
func resizeFile(source string, target string, size int) (string, error) {
file, err := os.Open(source)
if err != nil {
dlog("Unable to open the file")
return target, err
}
// Decode the file
file.Seek(0, 0)
img, _, err := image.Decode(file)
if err != nil {
dlog("Unable to decode the file from disk")
return target, err
}
file.Close()
// Resize it
m := resize.Resize(uint(size), 0, img, resize.Bicubic)
// Create a new, empty file
out, err := os.Create(target)
if err != nil {
dlog("Unable to save new file to disk")
return target, err
}
// Make sure the stream is closed once we're done here
defer out.Close()
// Save to data to disk
switch ftype := filepath.Ext(source); strings.ToLower(ftype) {
case ".jpg", ".jpeg":
jpeg.Encode(out, m, nil)
case ".png":
png.Encode(out, m)
case ".gif":
gif.Encode(out, m, nil)
}
return target, nil
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
path := UploadsPath + filepath.Base(r.URL.Path)
dlog(fmt.Sprintf("Searching for: %s\n", path))
// Check to see if the file exists
if _, err := os.Stat(path); err == nil {
dlog("File exists!")
// Check the size param
query := r.URL.Query()
s, ok := query["s"]
if !ok {
// No size param, serve the default file
http.ServeFile(w, r, path)
} else {
// Check that the size is allowed
// TODO: Handle error
targetSize, _ := strconv.Atoi(s[0])
if _, exists := AllowedSizes[targetSize]; exists {
// Check disk to see if the resized image exists
ext := filepath.Ext(path)
resizedpath := strings.Replace(path, ext, fmt.Sprintf("_s%d%s", targetSize, ext), -1)
if _, err := os.Stat(resizedpath); err == nil {
dlog("Resized file exists already")
// Serve the target file
http.ServeFile(w, r, resizedpath)
} else {
// Generate the file
if _, err := resizeFile(path, resizedpath, targetSize); err != nil {
// Something went wrong, serve the original path
http.ServeFile(w, r, path)
}
// Serve it
http.ServeFile(w, r, resizedpath)
}
}
}
} else {
dlog("File not found")
// TODO: Implement default image?
http.NotFound(w, r)
}
})
http.ListenAndServe(":9990", nil)
}