Browser-direct Google Cloud Storage resumable upload
Third member of the cloud-direct trio. The browser uploads each chunk straight to a GCS resumable session URL via Content-Range headers. Bytes never traverse Kestrel - the ASP.NET Core endpoint only signs the initiate. Survives reloads via IndexedDB: the resumed task issues a zero-byte PUT to the session URL, reads Range: bytes=0-N from GCS's 308 response, and continues.
What you should see: 8 MB chunks sent directly to Google Cloud Storage through a resumable session your server opens. Your application signs, but never handles the file.
Setup required. Like the S3 and Azure demos, this one needs real cloud credentials: register an
IGcsSigner so the server can open a resumable session. Without one the endpoint answers 501 GCS signer not registered and no bytes leave the browser — the transport itself is exercised, the destination is not. services.AddSingleton<IGcsSigner, YourGcsSigner>() in Program.cs, then restart the site.Drag & drop files here, or paste from clipboard
Resume behaviour: reload during an upload -> session URL restored from IndexedDB -> client probes GCS with
Content-Range: bytes */<total> -> GCS replies 308 Resume Incomplete with the byte offset it has -> uploading continues. No bytes re-sent.
Client config
CoreUpload.create('#uploader', {
uploadUrl: '/api/upload/upload',
strategy: 'gcs',
chunkSize: 8 * 1024 * 1024, // default 8 MiB; auto-rounded to 256 KiB grain persistState: true,
persistAdapter: 'indexeddb'
}); Server-side (Program.cs)
using CoreUpload.Providers;
builder.Services.AddSingleton<IGcsSigner>(_ => new MyGcsSigner(
bucketName: "your-bucket",
serviceAccountKeyJson: builder.Configuration["Gcs:ServiceAccountKey"]));
app.MapCoreUpload(); // /api/upload/* including /gcs/initiate, /gcs/finalize, /gcs/abort Wire protocol
POST /api/upload/gcs/initiate-> server creates a GCS resumable session, returns{ sessionUrl, key }PUT <sessionUrl><- chunked bytes withContent-Range: bytes <s>-<e>/<total>(browser-direct, bypasses Kestrel)308 Resume Incompleteon every chunk except the last (withRange: bytes=0-Nshowing committed offset)200/201 OKon the final chunk - body is the GCS object metadataPOST /api/upload/gcs/finalize-> optional server-side bookkeeping
Bucket CORS
[{
"origin": ["https://coreupload.com"],
"method": ["PUT", "OPTIONS"],
"responseHeader": ["Range", "Content-Range", "ETag"],
"maxAgeSeconds": 3600
}] When to pick GCS over S3
- Cheaper egress to Google services (BigQuery, Vertex AI, Cloud Run) - same-region transfer is free.
- Simpler protocol - one resumable session per file vs. S3's multipart sequence (initiate / sign N parts / complete).
- Object versioning + soft delete are first-class on GCS buckets.
- For S3-compatible stores (MinIO / R2 / B2 / Wasabi) use
strategy: 's3'instead.