Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/add-write-file-stream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@cloudflare/sandbox': patch
---

`writeFile` now accepts `string | ReadableStream<Uint8Array>` as content,
removing the 32 MiB size limit for file uploads. When a ReadableStream is
provided, it is consumed and streamed directly to disk with no buffering.
The original stream cannot be reused after the call.

The `encoding` option on `writeFile` is deprecated; prefer passing
a ReadableStream for binary data rather than base64-encoded strings.
199 changes: 199 additions & 0 deletions examples/stream-upload/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Created by https://www.toptal.com/developers/gitignore/api/macos,node,git
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,node,git

### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig

# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

### Node Patch ###
# Serverless Webpack directories
.webpack/

# Optional stylelint cache

# SvelteKit build / generate output
.svelte-kit

# End of https://www.toptal.com/developers/gitignore/api/macos,node,git

### Wrangler ###
.wrangler/
.env*
!.env.example
.dev.vars*
!.dev.vars.example
4 changes: 4 additions & 0 deletions examples/stream-upload/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM cloudflare/sandbox-test:0.7.18

# Required during local development to access exposed ports
EXPOSE 8080
59 changes: 59 additions & 0 deletions examples/stream-upload/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Stream Upload

**Upload files of any size to a sandbox via streaming, then download and verify integrity.**

A demo of the Sandbox SDK's `writeFile` and `readFileStream` APIs. When `writeFile` receives a `ReadableStream`, bytes are streamed directly to disk without base64 encoding or buffering, bypassing the 32 MiB size limit.

## Features

- Browser UI with file picker, upload, and SHA-256 verification
- Streaming upload via `writeFile` with a `ReadableStream` (no size limit)
- Streaming download via `readFileStream` + `streamFile`
- CLI test script for automated integrity checks

## Quick Start

```bash
npm install
npm run dev
```

Open http://localhost:8787 in your browser, pick a file, and click "Upload & Verify".

## How It Works

1. **Upload** - The browser sends the file as a raw binary stream via `POST /upload`. The Worker passes `request.body` directly to `sandbox.writeFile()`, which streams bytes to disk with zero buffering.

2. **Download** - The browser requests `GET /download?path=...`. The Worker calls `sandbox.readFileStream()` and pipes the decoded chunks into the response body.

3. **Verify** - The browser computes SHA-256 of both the original and downloaded bytes and compares them.

## API

| Endpoint | Method | Description |
| ----------- | ------ | -------------------------------------------------------------- |
| `/` | GET | Browser UI |
| `/upload` | POST | Stream a file to the sandbox. Query: `?filename=<name>` |
| `/download` | GET | Stream a file back from the sandbox. Query: `?path=<filepath>` |

## CLI Test Script

An automated test script is included for verifying the round-trip outside the browser:

```bash
# Default: 35 MB random file against localhost:8787
./test-upload.sh

# Custom server and size
./test-upload.sh http://localhost:8788 50
```

The script generates a random file with `dd`, uploads it, downloads it back, and compares SHA-256 hashes.

## Deploy

```bash
npm run deploy
```

After first deployment, wait 2-3 minutes for container provisioning before making requests.
22 changes: 22 additions & 0 deletions examples/stream-upload/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@cloudflare/sandbox-stream-upload-example",
"version": "1.0.0",
"type": "module",
"private": true,
"description": "Upload files to a sandbox via writeFileStream and verify integrity",
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev",
"start": "wrangler dev",
"cf-typegen": "wrangler types",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@cloudflare/sandbox": "*",
"@types/node": "^24.10.1",
"typescript": "^5.9.3",
"wrangler": "^4.70.0"
},
"author": "",
"license": "MIT"
}
Loading
Loading