Getting started
Your first program
This program builds an operator, writes a file, reads it back, inspects its metadata, and deletes it. It runs against the in-memory service with no credentials, so you can run it as soon as the binding is built.
using OpenDAL;
using System.Text;
// Configure a service, then build an operator from it.
using var op = new Operator("memory");
// The same verbs work on every service.
op.Write("hello.txt", Encoding.UTF8.GetBytes("Hello, World!"));
var bytes = op.Read("hello.txt");
Console.WriteLine(Encoding.UTF8.GetString(bytes));
var meta = op.Stat("hello.txt");
Console.WriteLine($"size = {meta.ContentLength} bytes");
op.Delete("hello.txt");
Write takes a byte[], and Read returns a byte[]. The using declaration
disposes the operator and frees its native handle when it goes out of scope —
always dispose Operator instances.
Point it at a real backend
Only the service changes; the operations stay identical. Pass the scheme and a dictionary of configuration keys:
using OpenDAL;
using System.Text;
using var op = new Operator("s3", new Dictionary<string, string>
{
["bucket"] = "my-bucket",
["region"] = "us-east-1",
});
op.Write("hello.txt", Encoding.UTF8.GetBytes("Hello from S3!"));
Console.WriteLine(Encoding.UTF8.GetString(op.Read("hello.txt")));
The binding bundles every service, so there is nothing extra to install. The next page, Connecting to your storage, covers construction and credentials in depth; Services lists every backend and its keys.
Async usage
Every operation has an …Async counterpart that returns a Task and accepts a
CancellationToken. Await each call:
using OpenDAL;
using System.Text;
using var op = new Operator("memory");
await op.WriteAsync("hello.txt", Encoding.UTF8.GetBytes("Hello, World!"));
var bytes = await op.ReadAsync("hello.txt");
Console.WriteLine(Encoding.UTF8.GetString(bytes));
Executors
Every operation runs on a native Tokio runtime. By default OpenDAL uses a shared
executor, so you can call any method without configuring anything. To control
the worker-thread count, create an Executor and bind it when you construct
the operator:
using OpenDAL;
using System.Text;
using var executor = new Executor(2); // two Tokio worker threads
using var op = new Operator("memory", executor: executor);
await op.WriteAsync("hello.txt", Encoding.UTF8.GetBytes("Hello, World!"));
var bytes = await op.ReadAsync("hello.txt");
The operator keeps its runtime alive on the native side, so disposing the
Executor handle never affects operators already bound to it — it only makes
constructing new operators with it throw ObjectDisposedException. One
operator binds one executor; to spread work across two runtimes, construct two
operators. See Going to production
for the lifetime rules.