Áttekintés
A storage egy önálló Go modul (github.com/gp-system/storage): object storage feltöltésekhez, avatarokhoz, generált riportokhoz, exportokhoz, mindenhez, ami nem egy adatbázis-sor. A shop mintaalkalmazásban az admin surface ezen keresztül tölt fel termékképeket. A gyökér modulnak nulla gp-system függősége van, és mindössze három 3rd-party függősége (go.opentelemetry.io/otel, .../metric, .../trace), így ha egy projekt egyébként nem is használja a kitet, ennek behúzása semmibe nem kerül az OTel saját API-csomagján túl. Bármilyen Go programban működik: CLI-eszközben, Lambdában, sima net/http service-ben, a gpsystem kittel vagy anélkül.
Telepítés
go get github.com/gp-system/storage@v0.1.0 # a kit is ezt a taget használja
go get github.com/gp-system/storage@latest
Go 1.23+ (a Disk Files iterátora range-over-func-ot használ). A beépített driverek külön importok: a storage/memory és a storage/local ugyanabban a modulban szállít, plusz függőségi költség nélkül. Az S3-kompatibilis driver más eset, lásd az S3 oldalt, hogy miért saját modul.
A modell: Driver, Disk, Manager
A csomag szándékosan három rétegre bomlik, mindegyiknek egy feladata van:
| Réteg | Mi ez | Mire használod |
|---|---|---|
Driver | a bővíthető backend-szerződés | egyszer implementálod backendenként; a legtöbb kód sosem hívja közvetlenül |
Disk | egy Driver köré épített instrumentált, ergonomikus wrapper | a service-rétegedben nap mint nap hívott API |
Manager | Disk-ek névvel ellátott gyűjteménye | akkor kell, ha egy projektben egynél több disk van |
Driver: a backend-szerződés
type Driver interface {
Name() string
Put(ctx context.Context, path string, r io.Reader, opts PutOptions) error
Reader(ctx context.Context, path string) (io.ReadCloser, error)
RangeReader(ctx context.Context, path string, offset, length int64) (io.ReadCloser, error)
Stat(ctx context.Context, path string) (FileInfo, error)
List(ctx context.Context, opts ListOptions) (ListPage, error)
Copy(ctx context.Context, src, dst string) error
Delete(ctx context.Context, path string) error
URL(ctx context.Context, path string) (string, error)
TemporaryURL(ctx context.Context, path string, opts TemporaryURLOptions) (string, error)
TemporaryUploadURL(ctx context.Context, path string, opts TemporaryUploadURLOptions) (UploadURL, error)
}
Egy Driver-nek nem kell mindent támogatnia: egy driver, ami nem tud aláírt URL-t adni, ErrUnsupported-ot ad vissza a TemporaryURL/TemporaryUploadURL-ből ahelyett, hogy hamisítana egyet, és aki ezzel törődik, ellenőrizni tudja (lásd Disk.ProvidesTemporaryURLs). Egy driver opcionálisan implementálhatja a Mover interfészt is:
type Mover interface {
Move(ctx context.Context, src, dst string) error
}
ha natívan tud objektumot mozgatni (átnevezés, nem bájtról bájtra másolás); a Disk.Move ezt használja, ha elérhető, egyébként Copy + Delete-re esik vissza.
Három beépített driver érkezik az ökoszisztémával: storage/memory (in-process, teszthez), storage/local (fájlrendszer, egy-node-os telepítéshez) és storage/driver/s3 (S3-kompatibilis, productionhöz). Mindhárom ugyanazt a Driver szerződést teljesíti, és a storage/storagetest ugyanazt a konformancia-suite-ot futtatja le mindháromra, plusz bármelyik saját írású driveredre.
ValidPath
func ValidPath(path string) bool
Egy kis guard, amit minden Driver-implementációtól elvárunk, hogy meghívjon, mielőtt hozzáér a backendhez: elutasítja az üres path-ot, az abszolút path-okat, a ./.. szegmenseket, és mindent, ami kiszökhetne a storage gyökeréből. Egy saját driver írásakor ezt kell meghívni a Put, Copy és társai elején, és ErrInvalidPath-t visszaadni, ha az eredmény false.
Disk: az API, amit ténylegesen hívsz
disk := storage.NewDisk(driver, storage.WithName("uploads"))
A NewDisk egy Driver-t csomagol be névvel és OTel-instrumentációval (span műveletenként, a disk nevével és a path-tal mint attribútummal), és ez az, amitől a service-kód nap mint nap függ; a teljes metódus-készletet lásd a Disk API oldalon. Opciók: WithName(string), WithTracerProvider, WithMeterProvider; ha az utóbbi kettőt nem állítod be, az instrumentáció no-op, mint mindenhol máshol a kitben.
Manager: több névvel ellátott disk
mgr := storage.New("public", map[string]*storage.Disk{
"public": storage.NewDisk(local.New(cfg), storage.WithName("public")),
"backups": storage.NewDisk(s3.New(s3cfg), storage.WithName("backups")),
})
mgr.Default().Put(ctx, "avatars/42.png", r, storage.PutOptions{})
backups, _ := mgr.Disk("backups")
A legtöbb projektnek egyetlen Disk elég. A Manager arra az esetre való, amikor egy app tényleg többet is használ: publikus assetek helyi lemezen, éjszakai adatbázis-dumpok S3-on, mondjuk. A New(defaultDiskName, disks) építi fel; a Default() a névvel megadott alapértelmezettet adja vissza, a Disk(name) név szerint keres (egy map-lookuppal azonos alakú ok bool-lal), a Names() pedig felsorolja az összes regisztráltat.
Dekorátorok: viselkedés komponálása egy Driverre
Mindkettő bármilyen Driver-t becsomagol, és egy másik Driver-t ad vissza, így komponálhatók:
d := storage.ReadOnly(s3.New(cfg)) // elutasítja az írásokat
scoped, err := storage.Scoped(s3.New(cfg), "tenant-42") // egy prefixre korlátoz
ReadOnly(Driver) Driver: mindenPut,DeleteésCopyhívás azonnalErrReadOnly-t ad vissza, anélkül hogy elérné a backendet. Hasznos egy olyan diskhoz, ami archivált vagy külsőleg kezelt tartalmat szolgál ki, ahol egy írni próbáló bug hangosan és lokálisan hibázzon, ne csendben sikerüljön a production storage ellen.Scoped(Driver, prefix string) (Driver, error): minden műveletet aprefixalatti path-okra korlátoz, transzparensen. Gyakori használat a tenant-izoláció egy megosztott bucketen: minden tenant kapjon egyDisk-et, amiScoped(sharedDriver, "tenants/"+tenantID)-ból épül, és egy bug, ami rossz path-ot állít össze az egyik tenanthoz, nem érhet el egy másik tenant fájljait, mert a scoped driver eleve nem lát a prefixén kívüli path-okat.
Mindkettő szabadon komponálható egymással és a beépített driverekkel, mivel egy Driver-t becsomagoló Driver maga is csak egy Driver.
Kapcsolódó oldalak
- Disk API: a teljes metódus-készlet: írások, olvasások, listázás, URL-ek.
- Memória és helyi lemez: a két driver, ami a gyökér modulban szállít.
- S3: az S3-kompatibilis driver, saját Go modulban.
- Tesztelés: a konformancia-suite, és saját driver írása.
- Architektúra: a három kimondott kivétel: miért a storage az egyik hely, ahol a kit interfész mögé rejt egy függőséget.