func (b *Bucket) DbDelImage(ctx context.Context, key string) (err error) {
	idxKey := b.DbIndexKey(key, nil)
	err = app.DB.Del(idxKey)  
	if err != nil {
		return
	}
	err = app.Chainstore.Del(context.Background(), idxKey)  
	return
}
TODO: should delete on *
package server
import (
	"context"
	"errors"
	"fmt"
	"regexp"
	"time"
	"github.com/goware/go-metrics"
	"github.com/goware/lg"
	"github.com/pressly/imgry"
)
var (
	ErrImageNotFound   = errors.New("image not found")
	ErrInvalidBucketID = errors.New("invalid bucket id - must be: [a-z0-9_:-] max-length: 40")
	BucketIDInvalidator = regexp.MustCompile(`(i?)[^a-z0-9\/_\-:\.]`)
)
type Bucket struct {
	ID string
}
func NewBucket(id string) (*Bucket, error) {
	b := &Bucket{ID: id}
	if _, err := b.ValidID(); err != nil {
		return nil, err
	}
	return b, nil
}
func (b *Bucket) ValidID() (bool, error) {
	if b.ID == "" || len(b.ID) > 40 {
		return false, ErrInvalidBucketID
	} else {
		if BucketIDInvalidator.MatchString(b.ID) {
			return false, ErrInvalidBucketID
		} else {
			return true, nil
		}
	}
}
func (b *Bucket) AddImagesFromUrls(ctx context.Context, urls []string) ([]*Image, error) {
	responses, err := app.Fetcher.GetAll(ctx, urls)
	if err != nil {
		return nil, err
	}
	images := make([]*Image, len(responses))
	for i, r := range responses {
		images[i] = NewImageFromSrcUrl(r.URL.String())
		defer images[i].Release()
		if r.Status == 200 && len(r.Data) > 0 {
			images[i].Data = r.Data
			if err = images[i].LoadImage(); err != nil {
				lg.Errorf("LoadBlob data for %s returned error: %s", r.URL.String(), err)
			}
		}
	}
	return images, b.AddImages(ctx, images)
}
func (b *Bucket) AddImages(ctx context.Context, images []*Image) (err error) {
	for _, i := range images {
		err = b.AddImage(ctx, i)
	}
	return err
}
func (b *Bucket) AddImage(ctx context.Context, i *Image) (err error) {
	if !i.IsValidImage() || len(i.Data) == 0 {
		return imgry.ErrInvalidImageData
	}
	err = b.DbSaveImage(ctx, i, nil)
	return
}
func (b *Bucket) GetImageSize(ctx context.Context, key string, sizing *imgry.Sizing) (*Image, error) {
	defer metrics.MeasureSince([]string{"fn.bucket.GetImageSize"}, time.Now())
	origIm, err := b.DbFindImage(ctx, key, nil)
	if err != nil {
		return nil, err
	}
	sizing.CalcResizeRect(&imgry.Rect{origIm.Width, origIm.Height})
	sizing.Size.Width = sizing.GranularizedWidth()
	sizing.Size.Height = sizing.GranularizedHeight()
	im, err := b.DbFindImage(ctx, key, sizing)
	if err != nil && err != ErrImageNotFound {
		return nil, err
	}
	if im != nil {  
		return im, nil
	}
	im2, err := origIm.MakeSize(sizing)
	defer im2.Release()
	if err != nil {
		return nil, err
	}
	err = b.DbSaveImage(ctx, im2, sizing)
	return im2, err
}
func (b *Bucket) DbFindImage(ctx context.Context, key string, optSizing ...*imgry.Sizing) (*Image, error) {
	defer metrics.MeasureSince([]string{"fn.bucket.DbFindImage"}, time.Now())
	var sizing *imgry.Sizing
	if len(optSizing) > 0 {  
		sizing = optSizing[0]
	}
	idxKey := b.DbIndexKey(key, sizing)
	im := &Image{}
	err := app.DB.HGet(idxKey, im)
	if err != nil {
		return nil, err
	}
	if im.Key == "" {
		return nil, ErrImageNotFound
	}
	data, err := app.Chainstore.Get(context.Background(), idxKey)  
	if err != nil {
		return nil, err
	}
	if len(data) == 0 {
		return nil, ErrImageNotFound
	}
	im.Data = data
	return im, nil
}
func (b *Bucket) DbSaveImage(ctx context.Context, im *Image, sizing *imgry.Sizing) (err error) {
	defer metrics.MeasureSince([]string{"fn.bucket.DbSaveImage"}, time.Now())
	if err := im.ValidateKey(); err != nil {
		return err
	}
	idxKey := b.DbIndexKey(im.Key, sizing)
	err = app.Chainstore.Put(context.Background(), idxKey, im.Data)  
	if err != nil {
		return
	}
	err = app.DB.HSet(idxKey, im)
	return
}