contented/thumb.go

115 lines
2.6 KiB
Go
Raw Normal View History

2017-11-18 00:11:39 +00:00
package contented
import (
"context"
2017-11-18 00:11:39 +00:00
"errors"
"fmt"
"log"
"net/http"
"path/filepath"
"code.ivysaur.me/thumbnail"
)
func getThumbnailerConfig(t byte) (*thumbnail.Config, error) {
2017-11-18 00:11:39 +00:00
// Modelled on what imgur.com offers
// @ref https://api.imgur.com/models/image#thumbs
opts := thumbnail.Config{
Aspect: thumbnail.FitOutside,
Output: thumbnail.Jpeg,
Scale: thumbnail.Bicubic,
}
2017-11-18 00:11:39 +00:00
switch t {
case 's':
opts.Width = 90
opts.Height = 90
2017-11-18 00:11:39 +00:00
case 'b':
opts.Width = 160
opts.Height = 160
2017-11-18 00:11:39 +00:00
case 't':
opts.Width = 160
opts.Height = 160
// thumbnail.ASPECT_RESPECT_MAX_DIMENSION_ONLY
2017-11-18 00:11:39 +00:00
case 'm':
opts.Width = 340
opts.Height = 340
// thumbnail.ASPECT_RESPECT_MAX_DIMENSION_ONLY
2017-11-18 00:11:39 +00:00
case 'l':
opts.Width = 640
opts.Height = 640
// thumbnail.ASPECT_RESPECT_MAX_DIMENSION_ONLY
2017-11-18 00:11:39 +00:00
case 'h':
opts.Width = 1024
opts.Height = 1024
// thumbnail.ASPECT_RESPECT_MAX_DIMENSION_ONLY
2017-11-18 00:11:39 +00:00
default:
return nil, errors.New("Unsupported thumbnail type (should be s/b/t/m/l/h)")
}
return &opts, nil
2017-11-18 00:11:39 +00:00
}
func (this *Server) handleThumb(w http.ResponseWriter, r *http.Request, thumbnailType byte, fileId string) {
ctx := r.Context()
opts, err := getThumbnailerConfig(thumbnailType)
2017-11-18 00:11:39 +00:00
if err != nil {
log.Printf("%s Thumbnail failed: %s\n", this.remoteIP(r), err.Error())
http.Error(w, err.Error(), 400)
return
2017-11-18 00:11:39 +00:00
}
// Only a limited number of thumbnails can be generated concurrently
select {
case <-this.thumbnailSem:
case <-r.Context().Done():
http.Error(w, r.Context().Err().Error(), 400) // probably won't be delivered anyway
return
}
defer func() { this.thumbnailSem <- struct{}{} }()
if ctx.Err() != nil {
// The request was already cancelled
return
}
t := thumbnail.NewThumbnailerEx(opts)
err = this.handleThumbInternal(ctx, w, t, fileId)
2017-11-18 00:11:39 +00:00
if err != nil {
log.Printf("%s Thumbnail failed: %s\n", this.remoteIP(r), err.Error())
w.Header().Set(`Location`, fmt.Sprintf(`/nothumb_%d.png`, opts.Height))
w.WriteHeader(302)
2017-11-18 00:11:39 +00:00
}
}
func (this *Server) handleThumbInternal(ctx context.Context, w http.ResponseWriter, t thumbnail.Thumbnailer, fileId string) error {
2017-11-18 00:11:39 +00:00
// Load metadata
m, err := this.Metadata(fileId)
if err != nil {
return err
}
if m.FileSize > this.opts.MaxThumbSizeBytes {
return errors.New("Don't want to thumbnail very large files, sorry")
}
2017-11-18 00:11:39 +00:00
filePath := filepath.Join(this.opts.DataDirectory, m.FileHash)
thumb, err := t.RenderFileAs(filePath, m.MimeType)
2017-11-18 00:11:39 +00:00
if err != nil {
return err
}
w.Header().Set(`Cache-Control`, `max-age=31536000, immutable`)
2017-11-18 00:11:39 +00:00
w.Header().Set(`Content-Length`, fmt.Sprintf("%d", len(thumb)))
w.Header().Set(`Content-Type`, `image/jpeg`)
w.WriteHeader(200)
w.Write(thumb)
return nil
}