The core package contract is a byte-oriented filesystem handle with explicit service selection.
library(Ropendal)
# Local filesystem via constructor.
root <- file.path(tempdir(), "ropendal-abstract-fs")
unlink(root, recursive = TRUE)
dir.create(root, recursive = TRUE)
fs <- opendal("fs", root = root)
# The same contract via URI. Put the local root in the query string so the
# example is portable across POSIX and Windows path syntax.
uri <- paste0("fs:///?root=", utils::URLencode(root, reserved = TRUE))
uri_fs <- opendal_uri(uri)
isTRUE(identical(fs_info(uri_fs)$scheme, fs_info(fs)$scheme))
#> [1] TRUEAll paths are normalized to the handle root before any operation, so clients can use portable relative paths.
Index-heavy formats can build a small range request object and pass it to the same read verbs. This keeps Zarr-like chunk readers, FASTA indexes, and other binary table lookups on the byte-first API.
fs_write(fs, "chunks.bin", as.raw(1:8))
#> [1] TRUE
idx <- data.frame(
path = "chunks.bin",
offset = c(0, 4),
size = c(4, 4),
chunk = c("0.0", "0.1")
)
req <- byte_ranges(idx$path, idx$offset, size = idx$size, id = idx$chunk)
fs_read(fs, req)
#> $`0.0`
#> [1] 01 02 03 04
#>
#> $`0.1`
#> [1] 05 06 07 08A byte store is a prefix adapter over the same filesystem handle. It is not a full array implementation; it provides store-relative keys for metadata and chunks so higher-level packages can build Zarr-like readers on top.
store <- byte_store(fs, "toy.zarr")
store_write(store, "zarr.json", charToRaw('{"zarr_format":3}'))
#> [1] TRUE
store_write(store, "c/0/0", as.raw(c(1, 2, 3, 4)))
#> [1] TRUE
store_list(store, recursive = TRUE)
#> [[1]]
#> [[1]]$path
#> [1] "c/0/0"
#>
#> [[1]]$type
#> [1] "file"
#>
#> [[1]]$size
#> [1] 4
#>
#> [[1]]$etag
#> NULL
#>
#> [[1]]$last_modified
#> [1] "2026-07-13T11:50:54.88497928Z"
#>
#> [[1]]$version
#> NULL
#>
#> [[1]]$content_type
#> NULL
#>
#> [[1]]$content_encoding
#> NULL
#>
#>
#> [[2]]
#> [[2]]$path
#> [1] "c/0/"
#>
#> [[2]]$type
#> [1] "dir"
#>
#> [[2]]$size
#> [1] 4096
#>
#> [[2]]$etag
#> NULL
#>
#> [[2]]$last_modified
#> [1] "2026-07-13T11:50:54.88497928Z"
#>
#> [[2]]$version
#> NULL
#>
#> [[2]]$content_type
#> NULL
#>
#> [[2]]$content_encoding
#> NULL
#>
#>
#> [[3]]
#> [[3]]$path
#> [1] "c/"
#>
#> [[3]]$type
#> [1] "dir"
#>
#> [[3]]$size
#> [1] 4096
#>
#> [[3]]$etag
#> NULL
#>
#> [[3]]$last_modified
#> [1] "2026-07-13T11:50:54.88497928Z"
#>
#> [[3]]$version
#> NULL
#>
#> [[3]]$content_type
#> NULL
#>
#> [[3]]$content_encoding
#> NULL
#>
#>
#> [[4]]
#> [[4]]$path
#> [1] "zarr.json"
#>
#> [[4]]$type
#> [1] "file"
#>
#> [[4]]$size
#> [1] 17
#>
#> [[4]]$etag
#> NULL
#>
#> [[4]]$last_modified
#> [1] "2026-07-13T11:50:54.882979282Z"
#>
#> [[4]]$version
#> NULL
#>
#> [[4]]$content_type
#> NULL
#>
#> [[4]]$content_encoding
#> NULL
as.raw(store_read(store, "c/0/0"))
#> [1] 01 02 03 04
# Add an explicit local full-object cache for repeated chunk reads.
cached_store <- store_cache(store, tempfile("ropendal-cache-"), validate = "none")
as.raw(collect_aio(store_read_aio(cached_store, "c/0/0")))
#> [1] 01 02 03 04runtime_config(), layer_concurrent_limit(),
and layer_timeout() are attached at construction time so
all operations share the same behavior.
Failed object creation returns values instead of throwing, which keeps remote control flow explicit.