Common tasks
Recipes for the things you actually do with storage. Each works on every
service. They assume an op built as in
Getting started. For full signatures, see the
API reference.
A few conventions used throughout:
- Paths are relative to the operator's root; a trailing
/means a directory (logs/app/is a directory,logs/appis a file). Readreturns[]byte;Writetakes[]byte.- Every operation returns an
erroras its last value — always check it. - Many verbs accept functional options (
opendal.ReadWithRange(...),opendal.WriteWithContentType(...), …) as trailing arguments.
Read a whole file
data, err := op.Read("path/to/file")
if err != nil {
log.Fatal(err)
}
text := string(data)
Read part of a file
ReadWithRange(offset, length) reads length bytes starting at offset:
data, err := op.Read("path/to/file", opendal.ReadWithRange(0, 1024))
Use opendal.ReadWithRangeFrom(offset) to read from offset to the end.
Stream a large file
Don't load gigabytes into memory — use a Reader, which implements
io.ReadSeekCloser, and read in chunks:
r, err := op.Reader("big.bin")
if err != nil {
log.Fatal(err)
}
defer r.Close()
buf := make([]byte, 8192)
for {
n, err := r.Read(buf)
if n > 0 {
// process buf[:n]
}
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
Write a whole file
err := op.Write("path/to/file", []byte("Hello, World!"))
Stream a large upload
Use a Writer for data produced incrementally. It implements io.WriteCloser;
call Write repeatedly, then Close to commit. Each Write call accepts up to
256KB.
w, err := op.Writer("big.bin")
if err != nil {
log.Fatal(err)
}
if _, err := w.Write(firstChunk); err != nil {
log.Fatal(err)
}
if _, err := w.Write(secondChunk); err != nil {
log.Fatal(err)
}
// Close finishes the write; data may be lost if you skip it.
if err := w.Close(); err != nil {
log.Fatal(err)
}
Upload concurrently
For large objects on services with multipart support, upload parts in parallel:
err := op.Write("big.bin", data, opendal.WriteWithConcurrent(8))
Check existence and metadata
exists, err := op.IsExist("path/to/file")
if err != nil {
log.Fatal(err)
}
if exists {
meta, err := op.Stat("path/to/file")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d bytes, dir = %v\n", meta.ContentLength(), meta.IsDir())
}
List a directory
List returns a Lister you iterate with Next and Entry. Always check
Error after the loop:
lister, err := op.List("dir/")
if err != nil {
log.Fatal(err)
}
defer lister.Close()
for lister.Next() {
entry := lister.Entry()
fmt.Println(entry.Path())
}
if err := lister.Error(); err != nil {
log.Fatal(err)
}
Walk a tree recursively
lister, err := op.List("dir/", opendal.ListWithRecursive(true))
The iteration is identical; only the option changes.
Delete a file or a whole tree
err := op.Delete("path/to/file") // single path
err = op.Delete("dir/", opendal.DeleteWithRecursive(true)) // path and everything under it
Delete succeeds even if the path does not exist.
Create a directory
err := op.CreateDir("path/to/dir/") // the trailing slash is required
Creating a directory that already exists succeeds, and creation is recursive
(like mkdir -p).
Copy and rename
err := op.Copy("from.txt", "to.txt")
err = op.Rename("old.txt", "new.txt")
Both operate within a single operator and require a service that supports them; see capability checks.
Generate a presigned URL
Hand a time-limited URL to a third party so they can access an object without
your credentials. The presign methods return a standard *net/http.Request:
import "time"
req, err := op.PresignRead("path/to/file", time.Hour)
if err != nil {
log.Fatal(err)
}
fmt.Println(req.Method, req.URL)
fmt.Println(req.Header)
PresignWrite, PresignStat, and PresignDelete work the same way for the
other operations.