Skip to main content

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 Readable streams (fs.createReadStream)
  • Web API ReadableStream (fetch responses)
  • Buffer or Uint8Array
  • Blob or File
  • 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`);