package store import ( "crypto/sha1" "encoding/hex" "errors" "fmt" "os" "path/filepath" "gitlab.celogeek.com/photos/api/internal/photoserrors" ) type Store struct { Path string } type Chunk struct { *Store Sum string Bytes []byte } func (s *Store) NewChunk(b []byte) *Chunk { sum := sha1.New() sum.Write(b) sumString := hex.EncodeToString(sum.Sum(nil)) return &Chunk{s, sumString, b} } func (s *Store) LoadChunk(sum string) (*Chunk, error) { c := &Chunk{s, sum, nil} if !c.FileExists() { return nil, fmt.Errorf("chunk %s doesn't exists", sum) } b, err := os.ReadFile(c.Filename()) if err != nil { return nil, err } c.Bytes = b return c, nil } func (c *Chunk) Dir() string { return filepath.Join(c.Path, "storage", c.Sum[0:1], c.Sum[1:2]) } func (c *Chunk) Filename() string { return filepath.Join(c.Dir(), c.Sum) } func (c *Chunk) FileExists() bool { fs, err := os.Stat(c.Filename()) if errors.Is(err, os.ErrNotExist) { return false } return !fs.IsDir() } func (c *Chunk) Mkdir() error { return os.MkdirAll(c.Dir(), 0755) } func (c *Chunk) Save() error { if c.FileExists() { return photoserrors.ErrStoreChunkAlreadyExists } if err := c.Mkdir(); err != nil { return err } fs, err := os.Create(c.Filename()) if err != nil { return err } defer fs.Close() _, err = fs.Write(c.Bytes) return err } func (s *Store) Combine(sumb []string) (string, uint64, error) { sum := sha1.New() size := uint64(0) for _, sb := range sumb { c, err := s.LoadChunk(sb) if err != nil { return "", 0, err } sum.Write(c.Bytes) size += uint64(len(c.Bytes)) } return hex.EncodeToString(sum.Sum(nil)), size, nil }