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
}]