Skip to main content

Multipart Upload Pipeline

For files larger than 50 MiB, Lioran S3 provides a resumable multipart upload pipeline supporting concurrent part chunking, automatic per-part retries, and atomic server-side assembly.


1. High-Level Automated Upload (uploadMultipart)​

The easiest way to upload large files is using bucket.uploadMultipart():

import { BastionClient } from "@liorans3/driver";

const client = new BastionClient("bastion://admin:YOUR_PASSWORD@127.0.0.1:27118");
const bucket = client.bucket("datasets");

const metadata = await bucket.uploadMultipart("large-dataset.tar.gz", "./data/dataset.tar.gz", {
contentType: "application/gzip",
partSize: 32 * 1024 * 1024, // 32 MiB chunks (minimum 5 MiB)
concurrency: 4, // 4 parallel worker threads
maxRetries: 3, // Retry failed chunks up to 3 times
customMetadata: {
dataset_version: "v2.4",
pipeline_run: "nightly-104",
},
onProgress: (p) => {
console.log(`Progress: ${p.percent}% (${p.bytesUploaded}/${p.totalBytes} bytes, Part ${p.partNumber}/${p.totalParts})`);
},
});

console.log(`Committed ${metadata.key} (${metadata.size_bytes} bytes, SHA-256: ${metadata.sha256_checksum})`);

2. Multipart Options Reference​

OptionTypeDefaultDescription
partSizenumber33554432 (32 MiB)Part size in bytes. Must be at least 5 MiB (5242880 bytes).
concurrencynumber4Number of simultaneous chunk uploads.
contentTypestring"application/octet-stream"MIME content type of the assembled object.
fileNamestring—Original filename metadata.
customMetadataRecord<string, string>—Custom key-value metadata saved in RocksDB.
maxRetriesnumber3Maximum retry attempts per part before aborting.
onProgress(p: MultipartUploadProgress) => void—Callback reporting bytes, percentage, and chunk metrics.
signalAbortSignal—Signal to cancel in-flight part uploads.

3. Low-Level Multipart Session Management​

For specialized workflows requiring manual part coordination:

// 1. Initialize session
const handle = await bucket.createMultipartUpload("custom-upload.bin", {
partSize: 10 * 1024 * 1024, // 10 MiB
contentType: "application/octet-stream",
});

console.log("Upload ID:", handle.uploadId);

// 2. Upload individual parts (1-indexed)
const part1Stream = fs.createReadStream("./part1.bin");
const p1 = await handle.uploadPart(1, part1Stream, { contentLength: 10 * 1024 * 1024 });
console.log(`Part 1 ETag: ${p1.etag}`);

const part2Stream = fs.createReadStream("./part2.bin");
const p2 = await handle.uploadPart(2, part2Stream, { contentLength: 5 * 1024 * 1024 });

// 3. Check session status
const status = await handle.status();
console.log("Verified Parts:", Object.keys(status.parts).length);

// 4. Finalize and assemble
const committed = await handle.complete();
console.log("Committed:", committed.key);

Aborting a Session​

To cancel an incomplete multipart upload and purge staged parts:

await handle.abort();

4. Resuming Interrupted Uploads​

If a large transfer disconnects or fails mid-stream, you can resume without re-uploading completed chunks:

import { MultipartUploadHandle, executeMultipartUpload } from "@liorans3/driver";

// Create handle for existing session
const handle = new MultipartUploadHandle(
{
upload_id: "0192a7b8-c9d0-7000-8000-000000000001",
bucket: "datasets",
key: "large-dataset.tar.gz",
recommended_part_size: 32 * 1024 * 1024,
max_parallel_parts: 4,
expires_at: "",
},
client["http"]
);

// Resumes upload: queries server for verified parts and uploads only missing parts
const committed = await executeMultipartUpload(handle, "./data/dataset.tar.gz", {
concurrency: 4,
onProgress: (p) => console.log(`Resume progress: ${p.percent}%`),
});