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.
Setup required. No
IS3Signer is registered yet, so
/ajaxupload.axd/s3/* currently returns 501. To activate this demo:
- Install
AWSSDK.S3 into bin/ (from NuGet).
- Move
v5-signers/DemoS3Signer.cs.template → App_Code/DemoS3Signer.cs.
- Fill in
DemoS3:* values in web.config.
- Recycle the app pool.
Full walk-through:
v5-signers/README.md.
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
}]