Direct-to-S3 multipart

Browser-direct S3 multipart upload

The browser uploads each part directly to Amazon S3 (or MinIO, Backblaze B2, Cloudflare R2, Wasabi — anything S3-API compatible) via presigned URLs your server signs on demand. The WebForms handler only signs — the bytes bypass IIS entirely. Survives reloads: the resumed task reuses the same uploadId and part ETags via IndexedDB.

Client config
AjaxUploader.create('#uploader', {
    uploadUrl: '/ajaxupload.axd/upload',
    strategy: 's3',                 // selects _s3DirectUpload()
    chunkSize: 5 * 1024 * 1024,     // S3 5 MB minimum part size
    chunkConcurrency: 4,            // parallel PUTs
    persistState: true,
    persistAdapter: 'indexeddb'     // resume survives reload
});
Server-side (Global.asax.cs)
using AjaxUploader.Providers;

protected void Application_Start(object sender, EventArgs e)
{
    // Your IS3Signer wraps AmazonS3Client (or MinIO client).
    S3.Signer = new MyS3Signer(
        bucketName: "your-bucket",
        region: "us-east-1",
        accessKeyId: ConfigurationManager.AppSettings["AWS:AccessKeyId"],
        secretKey: ConfigurationManager.AppSettings["AWS:SecretKey"]);
}
Wire protocol
  • POST /ajaxupload.axd/s3/create → { uploadId, key }
  • POST /ajaxupload.axd/s3/sign ← { uploadId, key, partNumbers } → { urls }
  • PUT <signedUrl> → S3 returns ETag per part (browser-direct)
  • POST /ajaxupload.axd/s3/complete ← { uploadId, key, parts:[{PartNumber, ETag}] }
  • POST /ajaxupload.axd/s3/abort on cancel / fatal error
Bucket CORS

The browser PUTs directly to S3, so the bucket must allow cross-origin PUT and expose the ETag header:

[{
  "AllowedOrigins": ["https://ajaxuploader.com"],
  "AllowedMethods": ["PUT"],
  "AllowedHeaders": ["*"],
  "ExposeHeaders": ["ETag"],
  "MaxAgeSeconds": 3000
}]