Skip to main content

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/app is a file).
  • Read returns []byte; Write takes []byte.
  • Every operation returns an error as 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.