implement prototype filesystem storage

This commit is contained in:
David Christofas
2020-03-05 17:13:19 +01:00
parent 0978653f90
commit f55199f01b
5 changed files with 116 additions and 32 deletions
+2
View File
@@ -62,6 +62,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) {
height, _ := strconv.Atoi(query.Get("height"))
fileType := query.Get("type")
filePath := query.Get("file_path")
etag := query.Get("etag")
encoder := thumbnails.EncoderForType(fileType)
if encoder == nil {
@@ -75,6 +76,7 @@ func (g Thumbnails) Thumbnails(w http.ResponseWriter, r *http.Request) {
Height: height,
ImagePath: filePath,
Encoder: encoder,
ETag: etag,
}
thumbnail := g.manager.GetStored(ctx)
+68 -1
View File
@@ -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
}
+22 -11
View File
@@ -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, "+")
}
+9 -5
View File
@@ -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
}
+15 -15
View File
@@ -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
}