From 34dcaf9ffe74fcf561be2ce19aa5e027a93fb2ad Mon Sep 17 00:00:00 2001 From: David Christofas Date: Wed, 4 Mar 2020 16:55:13 +0100 Subject: [PATCH] implement file sources If the file is not cached we have to get it from somewhere. For that I implemented a webdav file source which gets the file from reva. --- pkg/flagset/flagset.go | 4 +-- pkg/service/v0/service.go | 45 ++++++++++++++++++++++----- pkg/thumbnails/imgsource/imgsource.go | 33 ++++++++++++++++++++ pkg/thumbnails/imgsource/webdav.go | 41 ++++++++++++++++++++++++ pkg/thumbnails/thumbnails.go | 28 ++++++----------- 5 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 pkg/thumbnails/imgsource/imgsource.go create mode 100644 pkg/thumbnails/imgsource/webdav.go diff --git a/pkg/flagset/flagset.go b/pkg/flagset/flagset.go index 253f24d8b1..39d4005b7f 100644 --- a/pkg/flagset/flagset.go +++ b/pkg/flagset/flagset.go @@ -89,7 +89,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "debug-addr", - Value: "0.0.0.0:9114", + Value: "0.0.0.0:9194", Usage: "Address to bind debug server", EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, Destination: &cfg.Debug.Addr, @@ -115,7 +115,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringFlag{ Name: "http-addr", - Value: "0.0.0.0:9110", + Value: "0.0.0.0:9190", Usage: "Address to bind http server", EnvVars: []string{"THUMBNAILS_HTTP_ADDR"}, Destination: &cfg.HTTP.Addr, diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index e47c73c996..d652ecf759 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -8,6 +8,7 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/config" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache" + "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/imgsource" ) // Service defines the extension handlers. @@ -29,6 +30,9 @@ func NewService(opts ...Option) Service { manager: thumbnails.SimpleManager{ Cache: cache.NewInMemoryCache(), }, + source: imgsource.WebDav{ + Basepath: "http://localhost:9140/remote.php/webdav/", + }, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -43,6 +47,7 @@ type Thumbnails struct { config *config.Config mux *chi.Mux manager thumbnails.Manager + source imgsource.Source } // ServeHTTP implements the Service interface. @@ -53,27 +58,53 @@ func (g Thumbnails) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Thumbnails provides the endpoint to retrieve a thumbnail for an image func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() - width, _ := strconv.Atoi(query.Get("w")) - height, _ := strconv.Atoi(query.Get("h")) + width, _ := strconv.Atoi(query.Get("width")) + height, _ := strconv.Atoi(query.Get("height")) fileType := query.Get("type") - fileID := query.Get("file_id") + filePath := query.Get("file_path") encoder := thumbnails.EncoderForType(fileType) if encoder == nil { // TODO: better error responses + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("can't encode that")) return } ctx := thumbnails.ThumbnailContext{ Width: width, Height: height, - ImagePath: fileID, + ImagePath: filePath, Encoder: encoder, } - thumbnail, err := g.manager.Get(ctx) - if err != nil { - w.Write([]byte(err.Error())) + + thumbnail := g.manager.GetCached(ctx) + if thumbnail != nil { + w.Write(thumbnail) + return } + auth := r.Header.Get("Authorization") + + sCtx := imgsource.NewContext() + sCtx.Set(imgsource.WebDavAuth, auth) + // TODO: clean up error handling + img, err := g.source.Get(ctx.ImagePath, sCtx) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + if img == nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("img is nil")) + return + } + thumbnail, err = g.manager.Get(ctx, img) + if err != nil { + w.Write([]byte(err.Error())) + return + } + + w.WriteHeader(http.StatusCreated) w.Write(thumbnail) } diff --git a/pkg/thumbnails/imgsource/imgsource.go b/pkg/thumbnails/imgsource/imgsource.go new file mode 100644 index 0000000000..6697c9bdc0 --- /dev/null +++ b/pkg/thumbnails/imgsource/imgsource.go @@ -0,0 +1,33 @@ +package imgsource + +import "image" + +// Source defines the interface for image sources +type Source interface { + Get(path string, ctx SourceContext) (image.Image, error) +} + +// NewContext creates a new SourceContext instance +func NewContext() SourceContext { + return SourceContext{ + m: make(map[string]interface{}), + } +} + +// SourceContext is used to pass source specific parameters +type SourceContext struct { + m map[string]interface{} +} + +// GetString tries to cast the value to a string +func (s SourceContext) GetString(key string) string { + if s, ok := s.m[key].(string); ok { + return s + } + return "" +} + +// Set sets a value +func (s SourceContext) Set(key string, val interface{}) { + s.m[key] = val +} diff --git a/pkg/thumbnails/imgsource/webdav.go b/pkg/thumbnails/imgsource/webdav.go new file mode 100644 index 0000000000..5637d943db --- /dev/null +++ b/pkg/thumbnails/imgsource/webdav.go @@ -0,0 +1,41 @@ +package imgsource + +import ( + "fmt" + "image" + "net/http" + "net/url" + "path" +) + +// WebDav implements the Source interface for webdav services +type WebDav struct { + Basepath string +} + +const ( + // WebDavAuth is the parameter name for the autorization token + WebDavAuth = "Authorization" +) + +// Get downloads the file from a webdav service +func (s WebDav) Get(file string, ctx SourceContext) (image.Image, error) { + u, _ := url.Parse(s.Basepath) + u.Path = path.Join(u.Path, file) + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return nil, fmt.Errorf("could not get the file %s error: %s", file, err.Error()) + } + + auth := ctx.GetString(WebDavAuth) + req.Header.Add("Authorization", auth) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("could not get the file %s error: %s", file, err.Error()) + } + + img, _, _ := image.Decode(resp.Body) + return img, nil +} diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 07d163e7f5..5f1614adda 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -3,7 +3,6 @@ package thumbnails import ( "bytes" "image" - "os" "strings" "time" @@ -16,14 +15,13 @@ type ThumbnailContext struct { Width int Height int ImagePath string - - Encoder Encoder + Encoder Encoder } // Manager is responsible for generating thumbnails type Manager interface { // Get will return a thumbnail for a file - Get(ThumbnailContext) ([]byte, error) + Get(ThumbnailContext, image.Image) ([]byte, error) GetCached(ThumbnailContext) []byte } @@ -33,18 +31,14 @@ type SimpleManager struct { } // Get implements the Get Method of Manager -func (s SimpleManager) Get(ctx ThumbnailContext) ([]byte, error) { - key := buildCacheKey(ctx) +func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) { + thumbnail := s.generate(ctx, img) - cached := s.Cache.Get(key) - if cached == nil { - thumbnail := s.generate(ctx) - s.Cache.Set(key, thumbnail) - cached = thumbnail - } + key := buildCacheKey(ctx) + s.Cache.Set(key, thumbnail) buf := new(bytes.Buffer) - err := ctx.Encoder.Encode(buf, cached) + err := ctx.Encoder.Encode(buf, thumbnail) if err != nil { return nil, err } @@ -64,15 +58,11 @@ func (s SimpleManager) GetCached(ctx ThumbnailContext) []byte { return buf.Bytes() } -func (s SimpleManager) generate(ctx ThumbnailContext) image.Image { +func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Image { // TODO: remove, just for demo purposes time.Sleep(time.Second * 2) - // TODO: get file from reva - reader, _ := os.Open(ctx.ImagePath) - defer reader.Close() - m, _, _ := image.Decode(reader) - thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), m, resize.Lanczos2) + thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), img, resize.Lanczos2) return thumbnail }