Objects API & Streaming
The BucketHandle (obtained via client.bucket(name)) provides high-performance object operations.
1. Uploading Objects (put)
bucket.put() accepts a wide variety of payload types (PutData):
- Node.js
Readablestreams (fs.createReadStream) - Web API
ReadableStream(fetchresponses) BufferorUint8ArrayBloborFile- UTF-8
string
Uploading a Node.js Stream
import { BastionClient } from "@liorans3/driver";
import * as fs from "node:fs";
const client = new BastionClient("bastion://admin:YOUR_PASSWORD@127.0.0.1:27118");
const bucket = client.bucket("documents");
const stream = fs.createReadStream("./report.pdf");
const stat = fs.statSync("./report.pdf");
const meta = await bucket.put("finance/2026-q1.pdf", stream, {
contentType: "application/pdf",
contentLength: stat.size,
});
console.log(`Uploaded ${meta.key}`);
console.log(`ETag: ${meta.etag}`);
console.log(`SHA-256: ${meta.sha256_checksum}`);
Uploading Buffers or Text
// Upload Buffer
const buffer = Buffer.from("Raw binary content");
await bucket.put("data/binary.bin", buffer, {
contentType: "application/octet-stream",
});
// Upload String
await bucket.put("config/settings.json", JSON.stringify({ theme: "dark" }), {
contentType: "application/json",
});
2. Downloading Objects (get)
bucket.get() returns a BastionObject instance providing zero-copy streaming and conversion helpers.
Direct Download to File (Bounded Memory)
const object = await bucket.get("finance/2026-q1.pdf");
// Streams directly to disk without loading full file into RAM
await object.writeToFile("./downloads/downloaded-report.pdf");
Reading Content as Text or JSON
// Parse JSON
const jsonObj = await bucket.get("config/settings.json");
const settings = await jsonObj.json<{ theme: string }>();
console.log("Theme:", settings.theme);
// Read text
const textObj = await bucket.get("notes.txt");
const text = await textObj.text();
Reading Content as Buffer or Stream
const obj = await bucket.get("data/binary.bin");
// As Node.js Buffer
const buf = await obj.buffer();
// As Node.js Readable Stream
const nodeStream = obj.stream();
nodeStream.pipe(process.stdout);
3. Byte-Range Slicing (getRange)
Lioran S3 supports RFC 9110 / RFC 7233 partial byte-range downloads. The server seeks directly to the specified byte offset on disk:
// Download the first 1 MiB of an object (bytes 0 to 1048575)
const partial = await bucket.getRange("large-archive.tar", {
start: 0,
end: 1048575,
});
// Or request a suffix range (e.g., the last 500 bytes)
const footer = await bucket.getRange("large-archive.tar", {
suffix: 500,
});
console.log("Status:", partial.status); // 206 Partial Content
console.log("Content-Length:", partial.metadata.contentLength);
4. Querying Object Metadata (head)
Query object headers and existence without downloading the payload body (HTTP HEAD):
const headers = await bucket.head("finance/2026-q1.pdf");
console.log("Size:", headers.contentLength);
console.log("MIME Type:", headers.contentType);
console.log("SHA-256:", headers.sha256);
console.log("ETag:", headers.etag);
console.log("Supports Ranges:", headers.acceptRanges);
5. Listing Objects & Cursor Pagination
Single Page Query (listPage)
const page = await bucket.listPage({
prefix: "finance/", // Filter by key prefix
limit: 50, // Page size (1..1000)
cursor: undefined, // Optional cursor from previous page
});
for (const obj of page.objects) {
console.log(`- ${obj.key} (${obj.size_bytes} bytes)`);
}
if (page.has_more) {
console.log("Next cursor:", page.next_cursor);
}
Automatic Pagination (list)
To automatically paginate through all matching objects across multiple pages:
const allFinanceObjects = await bucket.list({ prefix: "finance/" });
console.log(`Found ${allFinanceObjects.length} total objects`);
6. Deleting Objects (delete)
const deleted = await bucket.delete("finance/2026-q1.pdf");
console.log("Deleted:", deleted);
7. Generating Presigned URLs
Generate HMAC-SHA256 expiring URLs for client-side uploads or downloads:
// 1. Presigned GET (Download link valid for 2 hours)
const downloadUrl = await bucket.presignGet("finance/2026-q1.pdf", {
expiresIn: 7200,
});
// 2. Presigned PUT (Direct browser upload valid for 15 minutes)
const uploadUrl = await bucket.presignPut("uploads/avatar.png", {
expiresIn: 900,
});
// 3. Presigned HEAD
const headUrl = await bucket.presignHead("finance/2026-q1.pdf", {
expiresIn: 1800,
});
8. Server-Side Image Optimization
Lioran S3 includes server-side image transformation (resizing, format conversion, and quality compression):
const result = await bucket.optimize("photos/hero.jpg", {
width: 1200,
height: 630,
fit: "fill", // "fit" | "fill" | "exact" | "thumbnail"
format: "webp", // "webp" | "jpeg" | "png"
quality: 85, // 1..100
targetKey: "photos/hero-1200x630.webp",
replaceOriginal: false,
});
console.log(`Optimized image saved to: ${result.variant.target_key}`);
console.log(`Size reduced from ${result.variant.original_size_bytes} to ${result.variant.optimized_size_bytes} bytes`);