first prototype of the thumbnail service
Currently uses in memory caching and loads the file from the local filesystem.
This commit is contained in:
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"image"
|
||||
)
|
||||
|
||||
// Cache defines the interface for a thumbnail cache.
|
||||
type Cache interface {
|
||||
Get(key string) image.Image
|
||||
Set(key string, thumbnail image.Image) (image.Image, error)
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package cache
|
||||
|
||||
// TODO: implement filesystem cache for longer persistence
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
package cache
|
||||
|
||||
import "image"
|
||||
|
||||
func NewInMemoryCache() InMemoryCache {
|
||||
return InMemoryCache{
|
||||
store: make(map[string]image.Image),
|
||||
}
|
||||
}
|
||||
|
||||
type InMemoryCache struct {
|
||||
store map[string]image.Image
|
||||
}
|
||||
|
||||
func (fsc InMemoryCache) Get(key string) image.Image {
|
||||
return fsc.store[key]
|
||||
}
|
||||
|
||||
func (fsc InMemoryCache) Set(key string, thumbnail image.Image) (image.Image, error) {
|
||||
fsc.store[key] = thumbnail
|
||||
return thumbnail, nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package thumbnails
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Encoder interface {
|
||||
Encode(io.Writer, image.Image) error
|
||||
Types() []string
|
||||
}
|
||||
|
||||
type PngEncoder struct{}
|
||||
|
||||
func (e PngEncoder) Encode(w io.Writer, i image.Image) error {
|
||||
return png.Encode(w, i)
|
||||
}
|
||||
|
||||
func (e PngEncoder) Types() []string {
|
||||
return []string{"png"}
|
||||
}
|
||||
|
||||
type JpegEncoder struct{}
|
||||
|
||||
func (e JpegEncoder) Encode(w io.Writer, i image.Image) error {
|
||||
return jpeg.Encode(w, i, nil)
|
||||
}
|
||||
|
||||
func (e JpegEncoder) Types() []string {
|
||||
return []string{"jpeg", "jpg"}
|
||||
}
|
||||
|
||||
func EncoderForType(fileType string) Encoder {
|
||||
switch strings.ToLower(fileType) {
|
||||
case "png":
|
||||
return PngEncoder{}
|
||||
case "jpg":
|
||||
fallthrough
|
||||
case "jpeg":
|
||||
return JpegEncoder{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package thumbnails
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/owncloud/ocis-thumbnails/pkg/thumbnails/cache"
|
||||
)
|
||||
|
||||
// ThumbnailContext bundles information needed to generate a thumbnail for afile
|
||||
type ThumbnailContext struct {
|
||||
Width int
|
||||
Height int
|
||||
ImagePath string
|
||||
|
||||
Encoder Encoder
|
||||
}
|
||||
|
||||
// Manager is responsible for generating thumbnails
|
||||
type Manager interface {
|
||||
// Get will return a thumbnail for a file
|
||||
Get(ThumbnailContext) ([]byte, error)
|
||||
GetCached(ThumbnailContext) []byte
|
||||
}
|
||||
|
||||
// SimpleManager is a simple implementation of Manager
|
||||
type SimpleManager struct {
|
||||
Cache cache.Cache
|
||||
}
|
||||
|
||||
// Get implements the Get Method of Manager
|
||||
func (s SimpleManager) Get(ctx ThumbnailContext) ([]byte, error) {
|
||||
key := buildCacheKey(ctx)
|
||||
|
||||
cached := s.Cache.Get(key)
|
||||
if cached == nil {
|
||||
thumbnail := s.generate(ctx)
|
||||
s.Cache.Set(key, thumbnail)
|
||||
cached = thumbnail
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := ctx.Encoder.Encode(buf, cached)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// GetCached tries to get the cached thumbnail and return it.
|
||||
// If there is no cached thumbnail it will return nil
|
||||
func (s SimpleManager) GetCached(ctx ThumbnailContext) []byte {
|
||||
key := buildCacheKey(ctx)
|
||||
cached := s.Cache.Get(key)
|
||||
if cached == nil {
|
||||
return nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
ctx.Encoder.Encode(buf, cached)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (s SimpleManager) generate(ctx ThumbnailContext) 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)
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
func buildCacheKey(ctx ThumbnailContext) string {
|
||||
parts := []string{
|
||||
ctx.ImagePath,
|
||||
string(ctx.Width) + "x" + string(ctx.Height),
|
||||
strings.Join(ctx.Encoder.Types(), ","),
|
||||
}
|
||||
return strings.Join(parts, "+")
|
||||
}
|
||||
Reference in New Issue
Block a user