-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement server /photos routing, get/post functionality #11
Conversation
* Add thumbnail functionality * Restructured server, added exif parsing, added fields to Photo * Fix exif value stripping of null character
The packages that this depends on should be added to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works! Mostly commented about some stuff to think about. I don't think anything is important enough to hold up merging, but I'll wait a bit for anyone to read them/comment.
PhotosDirectory: "/var/hotshots", | ||
} | ||
|
||
dir, ok := os.LookupEnv("HOTSHOTS_DIR") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably want to make ListenURL
configurable via env var too. Another time.
defer thumbw.Close() | ||
defer exifw.Close() | ||
|
||
mw := io.MultiWriter(photow, thumbw, exifw) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is some fancy stuff I've never seen before
go GetExif(&xif, exifr, saveErr) | ||
|
||
wasErrorSaving := false | ||
for saveProcesses := 0; saveProcesses < 3; saveProcesses++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using a sync.WaitGroup
here would be more idiomatic, but this does the job just fine.
Success bool | ||
Error string | ||
URI string | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We likely want to snake_case
all returned JSON fields with some struct tags, but it doesn't matter all that much.
} | ||
|
||
func (s *Server) PostPhoto(w http.ResponseWriter, r *http.Request) { | ||
r.ParseMultipartForm(1 << 23) // 8M max memory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we up this a bit? Anything up to maybe 20–30 MB would be a reasonable size for an uploaded JPEG, and if we up this a little bit (maybe 32 MB? Go's net/http
sets its defaultMaxMemory
to that) then we can avoid temp files touching the disk at all. We're not RAM-constrained on the server.
func GetExif(out *exif.Exif, data io.Reader, saveErr chan error) { | ||
x, err := exif.Decode(data) | ||
io.Copy(ioutil.Discard, data) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here err
is always io.EOF
if there is no Exif data in the photo, which causes a POST to /photos
to fail. We might want to check for that and just return an empty Exif data. This is not a dealbreaker because any reasonable camera embeds some Exif data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't a requirement for our service that your image had exif data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, maybe. That would make sense. It's a requirement now.
saveErr <- err | ||
return | ||
} | ||
*out = *x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kinda funky; I think the idiomatic thing to do would be to return the Exif data through a channel vs. C-style passing pointers to functions to modify them.
saveErr <- err | ||
return | ||
} | ||
*r = img.Bounds() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinda funky; see my comment on line 187.
return false | ||
} | ||
readErr := unix.Access(serv.cfg.PhotosDirectory, unix.R_OK) | ||
writeErr := unix.Access(serv.cfg.PhotosDirectory, unix.W_OK) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be done with os.Stat()
? So we're not relying on OS-specific stuff. (I may want to run Hotshots on my Windows XP SP2 box.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dear god
#3
Implemented Endpoints
GET /photos/
- Return array of all photo objects storedPOST /photos/
- Upload photoGET /photos/ids/
- Return array of all photo id stringsGET /photos/<id>/meta/
- Return photo object for the given idGET /photos/<id>/image.jpg
- Return jpg image data for the given idGET /photos/<id>/thumb.jpg
- Return jpg thumbnail data for the given id (created on upload)Files:
servers/server.go
- Contains server handling and setupservers/request.go
- Contains request handling logic for each routeservers/photo.go
- Contains photo metadata struct, photo processing logicservers/helpers.go
- Contains misc. logic