diff options
author | ᴜɴᴋɴᴡᴏɴ <u@gogs.io> | 2020-04-10 22:13:42 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-10 22:13:42 +0800 |
commit | 9a5b227f3ee2b2a3854d3aec022cc9a0cf0868b3 (patch) | |
tree | 7600826f4affce28e95588de921d801f0414f046 /internal/lfsutil/storage.go | |
parent | 3e055e329cf93eb5de77562d7795240808d31c08 (diff) |
lfsutil: add `Storager` interface and local storage (#6083)
* Add Storager interface
* Add tests
* Add back note
* Add tests for basic protocol routes
* Fix lint errors
Diffstat (limited to 'internal/lfsutil/storage.go')
-rw-r--r-- | internal/lfsutil/storage.go | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/internal/lfsutil/storage.go b/internal/lfsutil/storage.go index b2bfe37f..a357b8a8 100644 --- a/internal/lfsutil/storage.go +++ b/internal/lfsutil/storage.go @@ -5,9 +5,31 @@ package lfsutil import ( + "io" + "os" "path/filepath" + + "github.com/pkg/errors" + + "gogs.io/gogs/internal/osutil" ) +var ErrObjectNotExist = errors.New("Object does not exist") + +// Storager is an storage backend for uploading and downloading LFS objects. +type Storager interface { + // Storage returns the name of the storage backend. + Storage() Storage + // Upload reads content from the io.ReadCloser and uploads as given oid. + // The reader is closed once upload is finished. ErrInvalidOID is returned + // if the given oid is not valid. + Upload(oid OID, rc io.ReadCloser) (int64, error) + // Download streams content of given oid to the io.Writer. It is caller's + // responsibility the close the writer when needed. ErrObjectNotExist is + // returned if the given oid does not exist. + Download(oid OID, w io.Writer) error +} + // Storage is the storage type of an LFS object. type Storage string @@ -15,12 +37,73 @@ const ( StorageLocal Storage = "local" ) -// StorageLocalPath returns computed file path for storing object on local file system. -// It returns empty string if given "oid" isn't valid. -func StorageLocalPath(root string, oid OID) string { - if !ValidOID(oid) { +var _ Storager = (*LocalStorage)(nil) + +// LocalStorage is a LFS storage backend on local file system. +type LocalStorage struct { + // The root path for storing LFS objects. + Root string +} + +func (s *LocalStorage) Storage() Storage { + return StorageLocal +} + +func (s *LocalStorage) storagePath(oid OID) string { + if len(oid) < 2 { return "" } - return filepath.Join(root, string(oid[0]), string(oid[1]), string(oid)) + return filepath.Join(s.Root, string(oid[0]), string(oid[1]), string(oid)) +} + +func (s *LocalStorage) Upload(oid OID, rc io.ReadCloser) (int64, error) { + if !ValidOID(oid) { + return 0, ErrInvalidOID + } + + var err error + fpath := s.storagePath(oid) + defer func() { + rc.Close() + + if err != nil { + _ = os.Remove(fpath) + } + }() + + err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm) + if err != nil { + return 0, errors.Wrap(err, "create directories") + } + w, err := os.Create(fpath) + if err != nil { + return 0, errors.Wrap(err, "create file") + } + defer w.Close() + + written, err := io.Copy(w, rc) + if err != nil { + return 0, errors.Wrap(err, "copy file") + } + return written, nil +} + +func (s *LocalStorage) Download(oid OID, w io.Writer) error { + fpath := s.storagePath(oid) + if !osutil.IsFile(fpath) { + return ErrObjectNotExist + } + + r, err := os.Open(fpath) + if err != nil { + return errors.Wrap(err, "open file") + } + defer r.Close() + + _, err = io.Copy(w, r) + if err != nil { + return errors.Wrap(err, "copy file") + } + return nil } |