Cloudflare Workers-based chunked file transfer system documentation
This is a URL-to-Google Drive file transfer system built entirely on Cloudflare Workers. A user provides a direct download link and Google OAuth credentials. The system then transfers the file from the source server directly to Google Drive in 4 MiB chunks — without the file ever passing through the user's browser or home internet.
This system uses a hybrid architecture: the heavy data transfer is fully server-side (inside Cloudflare Workers), but the orchestration/loop control is browser-side (client JavaScript).
Each chunk follows this path — your browser is NOT in this path:
The browser only sends instructions and receives status updates:
Payload: ~200 bytes JSON request → ~30 bytes JSON response per chunk
| Responsibility | Backend Worker | Frontend Worker |
|---|---|---|
| Serve UI / HTML Page | ❌ | ✅ |
| Collect User Input | ❌ | ✅ |
| Get File Metadata (HEAD) | ❌ | ✅ |
| Google OAuth Token Exchange | ❌ | ✅ |
| Create Resumable Upload Session | ❌ | ✅ |
| Download File Chunk | ✅ | ❌ |
| Upload File Chunk to Drive | ✅ | ❌ |
| CORS Handling | ✅ | ❌ |
| Field | Type | Description |
|---|---|---|
sourceUrl | String | Direct download URL of the source file |
sessionUrl | String | Google Drive resumable upload session URL |
accessToken | String | Google OAuth2 Bearer access token |
contentLength | Number | Total file size in bytes |
chunkNumber | Number | 1-based chunk index to process |
| Google HTTP Status | Meaning | Worker Response |
|---|---|---|
| 308 | Chunk accepted, more needed | { status: "success" } |
| 200 / 201 | Final chunk, file complete | { status: "complete" } |
| Other | Error occurred | { status: "error", code: ... } |
User enters source URL, Google Client ID, Client Secret, and Refresh Token in the HTML form served by Frontend Worker.
Browser sends credentials to Frontend Worker. Frontend Worker performs a HEAD request on the source URL to get file size, MIME type, and filename.
Frontend Worker exchanges the refresh token for a fresh access token via Google's OAuth2 token endpoint.
Frontend Worker creates a resumable upload session with Google Drive and receives a unique session URL.
Frontend Worker returns all setup data to the browser: sourceUrl, filename, contentLength, accessToken, sessionUrl.
Browser calculates total chunks = ceil(contentLength / 4 MiB). Then loops from chunk 1 to N, sending a POST to Backend Worker for each chunk sequentially.
Backend Worker downloads the specific 4 MiB chunk from the source server using an HTTP Range request, then uploads it to Google Drive via PUT with Content-Range header.
When the final chunk returns status "complete", the browser shows a success message. The file is now in Google Drive.
Every chunk is exactly 4 MiB (4,194,304 bytes) except the last chunk, which may be smaller if the file size is not perfectly divisible by 4 MiB.
| Chunk # | Start Byte | End Byte |
|---|---|---|
| 1 | 0 | 4,194,303 |
| 2 | 4,194,304 | 8,388,607 |
| 3 | 8,388,608 | 12,582,911 |
| N (last) | (N-1) × 4,194,304 | contentLength - 1 |
Every single chunk gets its own separate, independent Worker invocation. Chunks do NOT share a single long-running invocation. This is by design to stay within Cloudflare Workers' per-request CPU and memory limits.
Example: A 20 MiB file = 5 chunks = 5 separate Backend Worker invocations
The upload is 100% sequential. The browser uses await inside the loop, meaning:
The actual file data never touches your home internet. Upload speed depends entirely on server-side factors:
How fast the source server can serve the file chunks
How fast Cloudflare can download from source and upload to Google
How fast Google Drive accepts the uploaded chunks
Latency between Cloudflare ↔ Source and Cloudflare ↔ Google
Since the chunk loop runs in client-side JavaScript, the browser tab must remain open until the entire upload finishes. Closing or refreshing the tab will stop the upload immediately. This is NOT a fully background/server-side system.
If any chunk fails, the upload stops with an error. There is no retry logic or checkpoint system. The entire upload must be restarted from scratch.
Google OAuth access tokens expire after ~1 hour. For very large files with many chunks, the token may expire mid-upload, causing a failure. There is no automatic token refresh during the upload loop.
OAuth credentials (Client ID, Client Secret, Refresh Token) are sent to Frontend Worker, and the Access Token is sent to Backend Worker. While all communication uses HTTPS, these credentials are exposed in browser memory and network requests.
The source server must support HTTP Range requests (partial content downloads) for chunked downloading to work. It must also return proper Content-Length headers on HEAD requests.
Each Worker invocation must complete within Cloudflare's CPU time and memory limits. With a 4 MiB chunk size, each invocation processes at most 4 MiB of data, which stays well within standard Worker limits.
No. The file data moves directly from Source Server → Cloudflare Worker → Google Drive. Your browser only sends small JSON instructions and receives small JSON status responses.
No. The upload speed depends entirely on the source server speed, Cloudflare's network speed, and Google Drive's acceptance speed. Your home internet only carries tiny control messages.
No. The browser runs the chunk loop. Closing it stops the upload immediately. The currently processing chunk might finish, but no new chunks will be requested.
No. Both Workers are in the same Cloudflare account but are not connected via any binding. The browser acts as the bridge — it calls Frontend Worker for setup and Backend Worker for each chunk upload.
Purely sequential. One chunk is fully downloaded and uploaded before the next one begins. There is zero parallelism.
The upload stops. An error message is displayed with the failed chunk number. There is no automatic retry. You must restart the entire process.
1 Frontend Worker invocation (setup) + N Backend Worker invocations (one per chunk) + possible CORS preflight invocations. For a 100 MiB file: 1 + 25 = at least 26 invocations.