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