Skip to content

Files and blobs

Task: store a large binary (a file, an image) for your app and read it back.

Blobs live outside the app’s state: the bytes go to the cluster’s sealed blob store, and the state keeps a BlobRef, the blob’s SHA-256. A reference in state is what keeps a blob: drop the reference and the blob goes. The store and its replication are in the blobs reference.

A client uploads to POST /apps/{app}/blobs and gets back the blob’s id (64 hex digits). It then calls a method with that id; a BlobRef parameter takes it as a string:

#[app::state(version = 1)]
pub struct Files {
/// The files kept, by name: a reference in state is what retains one.
kept: UnorderedMap<String, BlobRef>,
}
pub fn attach(&mut self, name: String, file: BlobRef) -> app::Result<()> {
let Some(info) = file.info() else {
app::bail!(Error::NotUploaded);
};
env::log(&format!("{name}: {} bytes of {}", info.size, info.mime));
self.kept.insert(&name, &file)?;
Ok(())
}

BlobRef::info() is None for a blob the app does not hold.

calimero_sdk::files writes and reads blobs from inside the app (apps/v2-fixtures/files, the sample guest):

use std::io::{Read, Write};
use calimero_sdk::files;
pub fn save_file(&mut self, data: Vec<u8>) -> app::Result<BlobRef> {
let mut writer = files::create()?;
writer.write_all(&data)?;
Ok(writer.finish()?)
}
#[app::view]
pub fn file_len(&self, id: BlobRef) -> app::Result<Option<u64>> {
Ok(id.info().map(|info| info.size))
}
  • files::create() returns a Writer (std::io::Write); finish() gives the BlobRef. A writer dropped unfinished leaves no file.
  • files::open(&blob) returns a Reader (std::io::Read), but only inside an #[app::compute] run: in a call, view, timer or init it is refused, and BlobRef::info gives the size and hash instead.
  • A file a failed call wrote is not kept: only a committed call’s files are.
#[app::view]
pub fn share(&self, file: BlobRef) -> app::Result<String> {
Ok(file.grant(3600))
}

grant(ttl_seconds) (1 to 86,400) returns a grant; a client downloads with GET /apps/{app}/blobs/{id}?grant=<grant>, signed in or not.