diff --git a/pkg/config/config.go b/pkg/config/config.go index 13b7a8ea3..3d66ec935 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -38,11 +38,11 @@ type Config struct { Debug Debug HTTP HTTP Tracing Tracing - FilesystemStorage FilesystemStorage + FileSystemStorage FileSystemStorage } -// FilesystemStorage defines the available filesystem storage configuration. -type FilesystemStorage struct { +// FileSystemStorage defines the available filesystem storage configuration. +type FileSystemStorage struct { RootDirectory string } diff --git a/pkg/service/v0/service.go b/pkg/service/v0/service.go index 88e4664fd..8c88cd612 100644 --- a/pkg/service/v0/service.go +++ b/pkg/service/v0/service.go @@ -28,7 +28,7 @@ func NewService(opts ...Option) Service { config: options.Config, mux: m, manager: thumbnails.SimpleManager{ - Storage: storage.NewInMemoryStorage(), + Storage: storage.NewFileSystemStorage(options.Config.FileSystemStorage), }, source: imgsource.WebDav{ Basepath: "http://localhost:9140/remote.php/webdav/", @@ -71,7 +71,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) { w.Write([]byte("can't encode that")) return } - ctx := thumbnails.ThumbnailContext{ + ctx := thumbnails.Context{ Width: width, Height: height, ImagePath: filePath, diff --git a/pkg/thumbnails/encoding.go b/pkg/thumbnails/encoding.go index 60a02f59b..8deb12871 100644 --- a/pkg/thumbnails/encoding.go +++ b/pkg/thumbnails/encoding.go @@ -8,31 +8,42 @@ import ( "strings" ) +// Encoder encodes the thumbnail to a specific format. type Encoder interface { + // Encode encodes the image to a format. Encode(io.Writer, image.Image) error + // Types returns the formats suffixes. Types() []string } +// PngEncoder encodes to png type PngEncoder struct{} +// Encode encodes to png format func (e PngEncoder) Encode(w io.Writer, i image.Image) error { return png.Encode(w, i) } +// Types returns the png suffix func (e PngEncoder) Types() []string { return []string{"png"} } +// JpegEncoder encodes to jpg. type JpegEncoder struct{} +// Encode encodes to jpg func (e JpegEncoder) Encode(w io.Writer, i image.Image) error { return jpeg.Encode(w, i, nil) } +// Types returns the jpg suffixes. func (e JpegEncoder) Types() []string { return []string{"jpeg", "jpg"} } +// EncoderForType returns the encoder for a given file type +// or nil if the type is not supported. func EncoderForType(fileType string) Encoder { switch strings.ToLower(fileType) { case "png": diff --git a/pkg/thumbnails/storage/filesystem.go b/pkg/thumbnails/storage/filesystem.go index e306a1149..e92e91bdf 100644 --- a/pkg/thumbnails/storage/filesystem.go +++ b/pkg/thumbnails/storage/filesystem.go @@ -11,12 +11,21 @@ import ( "github.com/owncloud/ocis-thumbnails/pkg/config" ) -type FileSystem struct { - cfg config.FilesystemStorage +// NewFileSystemStorage creates a new instanz of FileSystem +func NewFileSystemStorage(cfg config.FileSystemStorage) FileSystem { + return FileSystem{ + dir: cfg.RootDirectory, + } } +// FileSystem represents a storage for the thumbnails using the local file system. +type FileSystem struct { + dir string +} + +// Get loads the image from the file system. func (s FileSystem) Get(key string) []byte { - content, err := ioutil.ReadFile(filepath.Join(s.cfg.RootDirectory, key)) + content, err := ioutil.ReadFile(filepath.Join(s.dir, key)) if err != nil { return nil } @@ -24,11 +33,12 @@ func (s FileSystem) Get(key string) []byte { return content } +// Set writes the image to the file system. func (s FileSystem) Set(key string, img []byte) error { - path := filepath.Join(s.cfg.RootDirectory, key) - folder := filepath.Dir(path) - if err := createFolderIfNotExists(folder); err != nil { - return fmt.Errorf("error while creating folder %s", folder) + path := filepath.Join(s.dir, key) + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0700); err != nil { + return fmt.Errorf("error while creating directory %s", dir) } f, err := os.Create(path) @@ -44,7 +54,15 @@ func (s FileSystem) Set(key string, img []byte) error { return nil } -func (s FileSystem) BuildKey(ctx StorageContext) string { +// BuildKey generate the unique key for a thumbnail. +// The key is structure as follows: +// +// ///x. +// +// e.g. 97/9f/4c8db98f7b82e768ef478d3c8612/500x300.png +// +// The key also represents the path to the thumbnail in the filesystem under the configured root directory. +func (s FileSystem) BuildKey(ctx Context) string { etag := ctx.ETag filetype := ctx.Types[0] filename := strconv.Itoa(ctx.Width) + "x" + strconv.Itoa(ctx.Height) + "." + filetype @@ -60,13 +78,3 @@ func (s FileSystem) BuildKey(ctx StorageContext) string { return key.String() } - -func createFolderIfNotExists(folder string) error { - if _, err := os.Stat(folder); os.IsNotExist(err) { - err := os.MkdirAll(folder, 0700) - if err != nil { - return err - } - } - return nil -} diff --git a/pkg/thumbnails/storage/storage.go b/pkg/thumbnails/storage/storage.go index 4582433f2..8f5f3d9de 100644 --- a/pkg/thumbnails/storage/storage.go +++ b/pkg/thumbnails/storage/storage.go @@ -1,6 +1,7 @@ package storage -type StorageContext struct { +// Context combines different attributes needed for storage operations. +type Context struct { ETag string Types []string Width int @@ -11,5 +12,5 @@ type StorageContext struct { type Storage interface { Get(string) []byte Set(string, []byte) error - BuildKey(StorageContext) string + BuildKey(Context) string } diff --git a/pkg/thumbnails/thumbnails.go b/pkg/thumbnails/thumbnails.go index 10235f562..7172bacd4 100644 --- a/pkg/thumbnails/thumbnails.go +++ b/pkg/thumbnails/thumbnails.go @@ -3,14 +3,13 @@ package thumbnails import ( "bytes" "image" - "time" "github.com/nfnt/resize" "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage" ) -// ThumbnailContext bundles information needed to generate a thumbnail for afile -type ThumbnailContext struct { +// Context bundles information needed to generate a thumbnail for afile +type Context struct { Width int Height int ImagePath string @@ -21,8 +20,10 @@ type ThumbnailContext struct { // Manager is responsible for generating thumbnails type Manager interface { // Get will return a thumbnail for a file - Get(ThumbnailContext, image.Image) ([]byte, error) - GetStored(ThumbnailContext) []byte + Get(Context, image.Image) ([]byte, error) + // GetStored loads the thumbnail from the storage. + // It will return nil if no image is stored for the given context. + GetStored(Context) []byte } // SimpleManager is a simple implementation of Manager @@ -31,7 +32,7 @@ type SimpleManager struct { } // Get implements the Get Method of Manager -func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) { +func (s SimpleManager) Get(ctx Context, img image.Image) ([]byte, error) { thumbnail := s.generate(ctx, img) key := s.Storage.BuildKey(mapToStorageContext(ctx)) @@ -48,25 +49,19 @@ func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error // GetStored tries to get the stored thumbnail and return it. // If there is no cached thumbnail it will return nil -func (s SimpleManager) GetStored(ctx ThumbnailContext) []byte { +func (s SimpleManager) GetStored(ctx Context) []byte { key := s.Storage.BuildKey(mapToStorageContext(ctx)) stored := s.Storage.Get(key) - if stored == nil { - return nil - } return stored } -func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Image { - // TODO: remove, just for demo purposes - time.Sleep(time.Second * 2) - +func (s SimpleManager) generate(ctx Context, img image.Image) image.Image { thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), img, resize.Lanczos2) return thumbnail } -func mapToStorageContext(ctx ThumbnailContext) storage.StorageContext { - sCtx := storage.StorageContext{ +func mapToStorageContext(ctx Context) storage.Context { + sCtx := storage.Context{ ETag: ctx.ETag, Width: ctx.Width, Height: ctx.Height,