Direct-to-S3 / Azure tus 1.0 resume IndexedDB Golden Retriever 30 locales Webcam & screen capture

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 with Content-Range: bytes <s>-<e>/<total> (browser-direct, bypasses Kestrel)
  • 308 Resume Incomplete on every chunk except the last (with Range: bytes=0-N showing committed offset)
  • 200/201 OK on the final chunk - body is the GCS object metadata
  • POST /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.