implement prototype filesystem storage
This commit is contained in:
@@ -1,3 +1,70 @@
|
||||
package storage
|
||||
|
||||
// TODO: implement filesystem cache for longer persistence
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const BasePath = "/home/corby/tmp/thumbnails/fs/"
|
||||
|
||||
type FileSystem struct {
|
||||
}
|
||||
|
||||
func (s FileSystem) Get(key string) []byte {
|
||||
content, err := ioutil.ReadFile(BasePath + key)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
func (s FileSystem) Set(key string, img []byte) error {
|
||||
folder := filepath.Dir(BasePath + key)
|
||||
if err := createFolderIfNotExists(folder); err != nil {
|
||||
return fmt.Errorf("error while creating folder %s", folder)
|
||||
}
|
||||
|
||||
f, err := os.Create(BasePath + key)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s FileSystem) BuildKey(ctx StorageContext) string {
|
||||
etag := ctx.ETag
|
||||
filetype := ctx.Types[0]
|
||||
filename := strconv.Itoa(ctx.Width) + "x" + strconv.Itoa(ctx.Height) + "." + filetype
|
||||
|
||||
key := new(bytes.Buffer)
|
||||
key.WriteString(etag[:2])
|
||||
key.WriteRune('/')
|
||||
key.WriteString(etag[2:4])
|
||||
key.WriteRune('/')
|
||||
key.WriteString(etag[4:])
|
||||
key.WriteRune('/')
|
||||
key.WriteString(filename)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
package storage
|
||||
|
||||
import "image"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewInMemoryStorage() InMemoryStorage {
|
||||
return InMemoryStorage{
|
||||
store: make(map[string]image.Image),
|
||||
func NewInMemoryStorage() InMemory {
|
||||
return InMemory{
|
||||
store: make(map[string][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
type InMemoryStorage struct {
|
||||
store map[string]image.Image
|
||||
type InMemory struct {
|
||||
store map[string][]byte
|
||||
}
|
||||
|
||||
func (fsc InMemoryStorage) Get(key string) image.Image {
|
||||
return fsc.store[key]
|
||||
func (s InMemory) Get(key string) []byte {
|
||||
return s.store[key]
|
||||
}
|
||||
|
||||
func (fsc InMemoryStorage) Set(key string, thumbnail image.Image) (image.Image, error) {
|
||||
fsc.store[key] = thumbnail
|
||||
return thumbnail, nil
|
||||
func (s InMemory) Set(key string, thumbnail []byte) error {
|
||||
s.store[key] = thumbnail
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s InMemory) BuildKey(ctx StorageContext) string {
|
||||
parts := []string{
|
||||
ctx.ETag,
|
||||
string(ctx.Width) + "x" + string(ctx.Height),
|
||||
strings.Join(ctx.Types, ","),
|
||||
}
|
||||
return strings.Join(parts, "+")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"image"
|
||||
)
|
||||
type StorageContext struct {
|
||||
ETag string
|
||||
Types []string
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
// Storage defines the interface for a thumbnail store.
|
||||
type Storage interface {
|
||||
Get(key string) image.Image
|
||||
Set(key string, thumbnail image.Image) (image.Image, error)
|
||||
Get(string) []byte
|
||||
Set(string, []byte) error
|
||||
BuildKey(StorageContext) string
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package thumbnails
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
@@ -16,6 +15,7 @@ type ThumbnailContext struct {
|
||||
Height int
|
||||
ImagePath string
|
||||
Encoder Encoder
|
||||
ETag string
|
||||
}
|
||||
|
||||
// Manager is responsible for generating thumbnails
|
||||
@@ -34,28 +34,27 @@ type SimpleManager struct {
|
||||
func (s SimpleManager) Get(ctx ThumbnailContext, img image.Image) ([]byte, error) {
|
||||
thumbnail := s.generate(ctx, img)
|
||||
|
||||
key := buildKey(ctx)
|
||||
s.Storage.Set(key, thumbnail)
|
||||
key := s.Storage.BuildKey(mapToStorageContext(ctx))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := ctx.Encoder.Encode(buf, thumbnail)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
bytes := buf.Bytes()
|
||||
s.Storage.Set(key, bytes)
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// GetStored tries to get the stored thumbnail and return it.
|
||||
// If there is no stored thumbnail it will return nil
|
||||
// If there is no cached thumbnail it will return nil
|
||||
func (s SimpleManager) GetStored(ctx ThumbnailContext) []byte {
|
||||
key := buildKey(ctx)
|
||||
key := s.Storage.BuildKey(mapToStorageContext(ctx))
|
||||
stored := s.Storage.Get(key)
|
||||
if stored == nil {
|
||||
return nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
ctx.Encoder.Encode(buf, stored)
|
||||
return buf.Bytes()
|
||||
return stored
|
||||
}
|
||||
|
||||
func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Image {
|
||||
@@ -66,11 +65,12 @@ func (s SimpleManager) generate(ctx ThumbnailContext, img image.Image) image.Ima
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
func buildKey(ctx ThumbnailContext) string {
|
||||
parts := []string{
|
||||
ctx.ImagePath,
|
||||
string(ctx.Width) + "x" + string(ctx.Height),
|
||||
strings.Join(ctx.Encoder.Types(), ","),
|
||||
func mapToStorageContext(ctx ThumbnailContext) storage.StorageContext {
|
||||
sCtx := storage.StorageContext{
|
||||
ETag: ctx.ETag,
|
||||
Width: ctx.Width,
|
||||
Height: ctx.Height,
|
||||
Types: ctx.Encoder.Types(),
|
||||
}
|
||||
return strings.Join(parts, "+")
|
||||
return sCtx
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user