Guide — Handling large files
APULODI splits large files into parts and uploads them in parallel-ish sequence against a single session. This guide covers the failure modes you'll hit in the real world.
Do I need multipart?
- Files ≤ 8 MiB: the simple flow (one presigned PUT) is all you need —
apulodi.files.upload()uses it automatically. - Files > 8 MiB: the SDK switches to multipart automatically. You can
tune the threshold with
multipartThreshold.
await apulodi.files.upload({
file: bigVideo,
fileName: "gopro.mp4",
contentType: "video/mp4",
multipartThreshold: 16 * 1024 * 1024, // switch over sooner
});
Presigned URLs expire
Both the single-PUT URL and every part URL expire (default TTL is short for
security). If a PUT returns a 403 (signature expired), fetch a fresh URL:
const part = await apulodi.uploads.uploadPart(session, 7, chunk, {
contentType: "video/mp4",
});
// uploadPart regenerates the URL automatically when the old one expired
For the simple flow, call files.upload() again with the same idempotency
key to get a fresh URL without creating a second file.
Part count & ordering
The server validates on complete that:
- the number of parts equals
ceil(size / partSize), - part numbers are present and contiguous,
- the assembled object's size matches the declared size.
Uploading the same part twice or skipping a number results in 409 UPLOAD_PARTS_INCOMPLETE / UPLOAD_SIZE_MISMATCH. List what storage actually
holds to reconcile:
const held = await apulodi.uploads.listStoredParts(session.id);
for (const p of held) console.log(p.partNumber, p.etag, p.size);
Aborting a failed upload
The SDK aborts automatically when a part PUT fails:
try {
await apulodi.files.upload({ file, fileName: "x.bin", contentType: "application/octet-stream" });
} catch (error) {
// session was aborted for you; file is marked "failed"
}
To abandon an explicit session:
await apulodi.uploads.abort(session.id);
Abort is idempotent. In production, sessions that expire are cleaned up by a background job; orphaned storage parts are reaped automatically.
Retrying complete
If the app crashes after the parts are uploaded but before complete(), just
continue the session later — parts persist in storage:
// Re-create the session object from the stored session id:
const resumed = {
id: "upload_5c1a…",
partSize: 8388608,
parts: [], // URLs are regenerated on demand by uploadPart()
expiresAt: "",
};
const parts = await apulodi.uploads.listStoredParts(resumed.id);
const completed = parts.map((p) => ({ partNumber: p.partNumber, etag: p.etag }));
const file = await apulodi.uploads.complete(resumed.id, completed);
Next: return to the Introduction or browse the API reference.