Upload files
Upload an object directly to R2 with progress reporting and resumable intents.
upload() creates an intent through Carpo, sends the file directly to R2, then asks Carpo to verify and publish it. Carpo chooses single or multipart transfer based on the file size. The client handles part sizing, parallel transfers, progress, and completion.
const result = await storage.upload(file, {
key: `avatars/${userId}.png`,
overwrite: true,
metadata: {
contentType: 'image/png',
cacheControl: 'public, max-age=3600',
},
concurrency: 3,
onProgress: ({ loadedBytes, totalBytes, percent }) => {
console.log(`${percent}% (${loadedBytes} / ${totalBytes})`)
},
})upload() accepts a Blob or File. concurrency is the number of parallel multipart transfers and must be from 1 to 10. When progress reporting is enabled in a browser, the SDK uses XMLHttpRequest upload events by default.
Resume an interrupted upload
The SDK generates an idempotency key unless you provide one. When retrying a multipart upload, use the same key and the same object key, size, content type, overwrite value, and metadata. Carpo checks the existing intent and uploaded parts before transferring missing parts.
Use onIntent to persist both the intent ID and idempotency key as soon as the API creates or reuses the intent:
const upload = await storage.upload(file, {
key: `exports/${exportId}.zip`,
idempotencyKey: savedIdempotencyKey,
onIntent(intent, idempotencyKey) {
saveUploadProgress({ intentId: intent.id, idempotencyKey })
},
})If a transfer fails, CarpoStorageUploadError contains intentId and idempotencyKey. An AbortSignal cancels the current transfer but keeps the intent available to resume until it expires. Call abortUploadIntent(intentId) to discard the intent and its uploaded parts.
Build a custom transfer flow
For background jobs or custom transfer UI, use the lower level operations:
createUploadIntent()returns the intent and a single upload URL or multipart plan.- For multipart, call
getUploadIntent()to inspect completed parts andcreateUploadPartUrls()for the parts still needed. - Upload the bytes to the signed URLs.
- Call
completeUploadIntent()to ask Carpo to validate and publish the object. - Call
abortUploadIntent()when the upload should be discarded.
Signed browser transfers require R2 CORS rules for the app origin and the signed request methods and headers. If an upload fails in a browser, check the bucket CORS settings and the signed URL expiry.