Disk API
Disk (from storage.NewDisk) is the type service code depends on; nothing outside main.go needs to know which Driver sits behind it. Every method takes a context.Context and a slash-separated path, and every call is a traced OTel span, so a slow upload shows up in the trace waterfall right next to the DB queries it's near.
Writes
err := disk.Put(ctx, "avatars/42.png", r, storage.PutOptions{ContentType: "image/png"})
err := disk.PutBytes(ctx, "reports/2026-07.csv", data, storage.PutOptions{})
Put streams from an io.Reader, so a large upload never sits fully in memory. PutBytes is the convenience form for data you already hold as a []byte (a generated report, a small in-memory buffer): it wraps a bytes.Reader and calls Put. PutOptions carries the metadata a driver may act on (content type, and whatever else a specific driver's options support); a driver that doesn't preserve some of it is a valid, if less capable, implementation, and storagetest lets you assert whether it does.
Reads
b, err := disk.Get(ctx, "avatars/42.png") // whole object, into memory
rc, err := disk.Reader(ctx, "avatars/42.png") // streamed; caller must Close()
rc, err := disk.RangeReader(ctx, "video.mp4", 0, 1<<20) // first MiB only
info, err := disk.Stat(ctx, "avatars/42.png")
ok := disk.Exists(ctx, "avatars/42.png")
missing := disk.Missing(ctx, "avatars/42.png")
Get is the convenience form for small objects: it reads the whole thing into memory and closes the reader for you. For anything large, Reader streams instead, and the caller owns closing it. RangeReader opens a byte range [offset, offset+length), for resumable downloads or serving HTTP Range requests without reading the whole object. Stat returns a FileInfo (size, content type, last-modified time, and so on) without transferring the body: a HEAD-style check. Exists/Missing are boolean conveniences over the same call, for the common case where you only care whether the object is there, not its metadata; a genuine backend error from the underlying Stat is treated as "not confirmed to exist" rather than panicking or propagating silently, so check Stat directly if you need to distinguish "missing" from "storage is unreachable."
Listing
page, err := disk.List(ctx, storage.ListOptions{Prefix: "avatars/", After: cursor})
List returns one page of FileInfo under ListOptions.Prefix, with ListOptions.After/ListPage.ContinuationToken carrying you to the next page: meant for reconciliation and GC sweeps, not request-serving code paths one object at a time.
For walking an entire prefix without hand-rolling the pagination loop, Files is a Go 1.23+ range-over-func iterator:
for info, err := range disk.Files(ctx, "avatars/") {
if err != nil {
return err
}
fmt.Println(info.Path, info.Size)
}
Files pages through List internally and yields one FileInfo at a time; breaking out of the loop early (a break, a return) simply stops fetching further pages.
Copy, Delete, Move
err := disk.Copy(ctx, "uploads/staging/x.png", "avatars/42.png")
err := disk.Delete(ctx, "uploads/staging/x.png")
err := disk.Move(ctx, "uploads/staging/x.png", "avatars/42.png")
Copy is a server-side copy where the driver supports one, without the bytes passing through your process. Delete is idempotent: deleting a path that isn't there is not an error. Move uses the driver's native move when it implements the optional Mover interface (see Overview); otherwise Disk does Copy followed by Delete itself, so callers never need to know which case they're in.
URLs
url, err := disk.URL(ctx, "avatars/42.png")
signed, err := disk.TemporaryURL(ctx, "exports/orders.csv", storage.TemporaryURLOptions{TTL: 15 * time.Minute})
upload, err := disk.TemporaryUploadURL(ctx, "uploads/42.png", storage.TemporaryUploadURLOptions{TTL: 5 * time.Minute})
if disk.ProvidesTemporaryURLs() {
// safe to offer direct-to-storage download/upload links
}
URL is for public content: a stable link, typically backed by a public bucket or a CDN base URL. TemporaryURL hands out a time-limited, signed link for private content, so the client downloads straight from storage rather than proxying through your service. TemporaryUploadURL is the equivalent for direct client uploads, returning an UploadURL (the URL, and whatever headers or fields the caller needs to attach to the request, since that varies by driver). Not every driver can sign URLs; ProvidesTemporaryURLs is a capability check you can use to branch instead of calling the method and handling ErrUnsupported after the fact.
Related pages
- Overview: the Driver/Disk/Manager model, decorators.
- Memory and local disk: drivers to back a
Diskwith, for tests or a single-server deployment. - S3: the S3-compatible driver, and its own presigned URL support.
- Testing: the conformance suite that verifies a
Driverbehaves consistently.