Browser-direct Google Cloud Storage resumable upload
Third member of the cloud-direct trio (S3, Azure, GCS). The browser uploads each chunk straight to a GCS resumable session URL via Content-Range headers. Bytes bypass your server entirely - your backend only signs the initiate. Survives reloads via IndexedDB: the resumed task probes GCS with a zero-byte PUT, reads Range: bytes=0-N from the 308 response, and continues from byte N+1.
Resume contract: the GCS session URL is persisted in IndexedDB. On resume, a zero-byte PUT to the session URL with
Content-Range: bytes */<total> returns 308 Resume Incomplete with the byte offset GCS holds. The client picks up from there. Identical model to tus.
Client config
new MultipleUpload('#uploader', {
uploadUrl: '/api/upload',
strategy: 'gcs',
chunkSize: 8 * 1024 * 1024, // default 8 MiB; auto-rounded to 256 KiB grain
persistState: true,
persistAdapter: 'indexeddb',
// Optional: override the default endpoint paths
// gcsInitiateUrl: '/my/gcs/initiate',
// gcsFinalizeUrl: '/my/gcs/finalize',
// gcsAbortUrl: '/my/gcs/abort',
});Wire protocol
POST /gcs/initiate- your server creates a GCS resumable session via the official SDK, returns{ sessionUrl, key }. The browser never sees the GCS access token.PUT <sessionUrl><- chunked bytes withContent-Range: bytes <s>-<e>/<total>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 /gcs/finalize- your bookkeeping hook (record GCS key in DB, etc.)
Node server snippet (initiate)
// /gcs/initiate handler - using @google-cloud/storage
import { Storage } from '@google-cloud/storage';
const storage = new Storage();
const bucket = storage.bucket('your-bucket');
app.post('/gcs/initiate', async (req, res) => {
const { fileName, fileSize, contentType } = req.body;
const file = bucket.file(`uploads/${crypto.randomUUID()}/${fileName}`);
const [sessionUrl] = await file.createResumableUpload({ metadata: { contentType } });
res.json({ sessionUrl, key: file.name });
});Bucket CORS
[{
"origin": ["https://your-site.com"],
"method": ["PUT", "OPTIONS"],
"responseHeader": ["Range", "Content-Range", "ETag"],
"maxAgeSeconds": 3600
}]Apply with gsutil cors set cors.json gs://your-bucket.
Cloud trio at a glance
| Strategy | Best for | Chunk grain | Concurrency |
|---|---|---|---|
's3' | S3, MinIO, R2, B2, Wasabi | 5 MiB min part | Parallel parts |
'azure' | Azure Blob | 4 MiB blocks | Parallel blocks |
'gcs' | Google Cloud Storage | 256 KiB grain | Sequential (protocol) |