Direct-to-cloud setup
CoreUpload deliberately takes no SDK dependency. You implement a small signer interface with whichever SDK you already use; the endpoints and the browser strategies do the rest. Credentials never reach the client.
1. Implement a signer
For S3 (works for MinIO, Backblaze B2, Cloudflare R2, Wasabi — anything S3-compatible):
public class MyS3Signer : IS3Signer
{
private readonly IAmazonS3 _s3; // your SDK, your credentials
private const string Bucket = "my-uploads";
public async Task<S3CreateResult> CreateMultipartUploadAsync(S3CreateRequest r, CancellationToken ct = default)
{
var key = $"incoming/{Guid.NewGuid()}/{r.FileName}";
var res = await _s3.InitiateMultipartUploadAsync(Bucket, key, ct);
return new S3CreateResult { UploadId = res.UploadId, Key = key };
}
public Task<S3SignResult> SignPartsAsync(S3SignRequest r, CancellationToken ct = default)
{
var urls = r.PartNumbers.ToDictionary(
n => n.ToString(),
n => _s3.GetPreSignedURL(new GetPreSignedUrlRequest {
BucketName = Bucket, Key = r.Key, UploadId = r.UploadId,
PartNumber = n, Verb = HttpVerb.PUT,
Expires = DateTime.UtcNow.AddMinutes(15)
}));
return Task.FromResult(new S3SignResult { Urls = urls });
}
// CompleteMultipartUploadAsync, AbortMultipartUploadAsync,
// and optionally ListPartsAsync (server-verified resume) follow the same shape.
} ListPartsAsync is optional (a default interface member). Implement it and resumed sessions reconcile against what S3 actually holds instead of trusting persisted client state.
2. Register + choose the strategy
// Program.cs
builder.Services.AddSingleton<IS3Signer, MyS3Signer>(); // or IAzureSigner / IGcsSigner
app.MapCoreUploadEndpoints(); // /s3/create, /s3/sign, /s3/list-parts, /s3/complete, /s3/abort <core-upload asp-upload-url="/api/upload/upload"
asp-strategy="s3"
asp-chunk-size="8MB"
asp-chunk-concurrency="4"></core-upload> No signer registered? The endpoints answer 501 with a clear message rather than failing mysteriously.
3. Provider cheat-sheet
| Provider | Interface | Strategy | Must-know |
|---|---|---|---|
| S3-compatible | IS3Signer | s3 | Parts ≥ 5 MB; expose ETag in bucket CORS; lifecycle-abort stale multiparts. |
| Azure Blob | IAzureSigner | azure | Short-lived SAS; blocks are invisible until PutBlockList commits. |
| Google Cloud Storage | IGcsSigner | gcs | Chunks in 256 KiB multiples (aligned automatically); session URLs expire. |
4. Bucket CORS (the usual blocker)
// S3 example
[{
"AllowedOrigins": ["https://app.example.com"],
"AllowedMethods": ["PUT", "POST"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"], // multipart completion needs this
"MaxAgeSeconds": 3600
}]