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.
This commit is contained in:
David Christofas
2020-03-04 16:55:13 +01:00
parent a98df0b11f
commit 34dcaf9ffe
5 changed files with 123 additions and 28 deletions

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}