← Sandbox SDK / sandbox / tutorials
Persistentní ukládání dat pomocí R2
Připojte buckety objektového úložiště jako cesty v lokálním souborovém systému a uchovejte data napříč životními cykly sandboxu. Tento návod používá Cloudflare R2, ale stejný postup funguje u libovolného poskytovatele kompatibilního s S3.
Tento návod ukazuje, jak zachovat externí datový adresář připojený na /data. Pokud chcete mít pracovní projekt v /workspace aby zůstaly zachovány, viz Zálohování a obnova.
Doba dokončení: 20 minut
Co vytvoříte
Worker, který zpracovává data, ukládá výsledky do bucketu R2 připojeného jako lokální adresář a ukazuje, že data přetrvají i po zničení a opětovném vytvoření sandboxu.
Klíčové koncepty, které se naučíte:
- Připojení bucketů R2 jako cest v souborovém systému
- Automatická perzistence dat napříč životními cykly sandboxu
- Práce s připojeným úložištěm pomocí standardních operací se soubory
Předpoklady
- Zaregistrujte si účet Cloudflare ↗.
- Nainstalujte
Node.js↗.
Správce verzí Node.js
Použijte správce verzí Node, jako je Volta ↗ nebo nvm ↗ abyste se vyhnuli problémům s oprávněními a mohli měnit verze Node.js. Wrangler, o kterém se dozvíte dále v této příručce, vyžaduje verzi Node 16.17.0 nebo novější.
Budete také potřebovat:
- Docker ↗ při lokálním spuštění
- R2 bucket (jeden vytvořte v Cloudflare dashboard ↗)
1. Vytvořte projekt
npm create cloudflare@latest -- data-pipeline --template=cloudflare/sandbox-sdk/examples/minimalcd data-pipeline2. Nakonfigurujte R2 binding
Přidejte binding R2 bucket do svého wrangler.json:
{
"name": "data-pipeline",
"compatibility_date": "2025-11-09",
"durable_objects": {
"bindings": [
{ "name": "Sandbox", "class_name": "Sandbox" }
]
},
"r2_buckets": [
{
"binding": "DATA_BUCKET",
"bucket_name": "my-data-bucket"
}
]
}Nahraďte my-data-bucket svým názvem R2 bucketu. Bucket nejprve vytvořte v Cloudflare dashboard ↗.
3. Sestavte procesor dat
Nahraďte src/index.ts s kódem, který připojí R2 a zpracovává data:
import { getSandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request, env) {
const url = new URL(request.url);
const sandbox = getSandbox(env.Sandbox, "data-processor");
// Mount R2 bucket to /data directory
await sandbox.mountBucket("my-data-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
});
if (url.pathname === "/process") {
// Process data and save to mounted R2
const result = await sandbox.exec("python", {
args: [
"-c",
`
import json
import os
from datetime import datetime
# Read input (or create sample data)
data = [
{'id': 1, 'value': 42},
{'id': 2, 'value': 87},
{'id': 3, 'value': 15}
]
# Process: calculate sum and average
total = sum(item['value'] for item in data)
avg = total / len(data)
# Save results to mounted R2 (/data is the mounted bucket)
result = {
'timestamp': datetime.now().isoformat(),
'total': total,
'average': avg,
'processed_count': len(data)
}
os.makedirs('/data/results', exist_ok=True)
with open('/data/results/latest.json', 'w') as f:
json.dump(result, f, indent=2)
print(json.dumps(result))
`,
],
});
return Response.json({
message: "Data processed and saved to R2",
result: JSON.parse(result.stdout),
});
}
if (url.pathname === "/results") {
// Read results from mounted R2
const result = await sandbox.exec("cat", {
args: ["/data/results/latest.json"],
});
if (!result.success) {
return Response.json(
{ error: "No results found yet" },
{ status: 404 },
);
}
return Response.json({
message: "Results retrieved from R2",
data: JSON.parse(result.stdout),
});
}
if (url.pathname === "/destroy") {
// Destroy sandbox to demonstrate persistence
await sandbox.destroy();
return Response.json({
message: "Sandbox destroyed. Data persists in R2!",
});
}
return new Response(
`
Data Pipeline with Persistent Storage
Endpoints:
- POST /process - Process data and save to R2
- GET /results - Retrieve results from R2
- POST /destroy - Destroy sandbox (data survives!)
Try this flow:
1. POST /process (processes and saves to R2)
2. POST /destroy (destroys sandbox)
3. GET /results (data still accessible from R2)
`,
{ headers: { "Content-Type": "text/plain" } },
);
},
};import { getSandbox, type Sandbox } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
interface Env {
Sandbox: DurableObjectNamespace<Sandbox>;
DATA_BUCKET: R2Bucket;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const sandbox = getSandbox(env.Sandbox, 'data-processor');
// Mount R2 bucket to /data directory
await sandbox.mountBucket('my-data-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com'
});
if (url.pathname === '/process') {
// Process data and save to mounted R2
const result = await sandbox.exec('python', {
args: ['-c', `
import json
import os
from datetime import datetime
# Read input (or create sample data)
data = [
{'id': 1, 'value': 42},
{'id': 2, 'value': 87},
{'id': 3, 'value': 15}
]
# Process: calculate sum and average
total = sum(item['value'] for item in data)
avg = total / len(data)
# Save results to mounted R2 (/data is the mounted bucket)
result = {
'timestamp': datetime.now().isoformat(),
'total': total,
'average': avg,
'processed_count': len(data)
}
os.makedirs('/data/results', exist_ok=True)
with open('/data/results/latest.json', 'w') as f:
json.dump(result, f, indent=2)
print(json.dumps(result))
`]
});
return Response.json({
message: 'Data processed and saved to R2',
result: JSON.parse(result.stdout)
});
}
if (url.pathname === '/results') {
// Read results from mounted R2
const result = await sandbox.exec('cat', {
args: ['/data/results/latest.json']
});
if (!result.success) {
return Response.json({ error: 'No results found yet' }, { status: 404 });
}
return Response.json({
message: 'Results retrieved from R2',
data: JSON.parse(result.stdout)
});
}
if (url.pathname === '/destroy') {
// Destroy sandbox to demonstrate persistence
await sandbox.destroy();
return Response.json({ message: 'Sandbox destroyed. Data persists in R2!' });
}
return new Response(`
Data Pipeline with Persistent Storage
Endpoints:
- POST /process - Process data and save to R2
- GET /results - Retrieve results from R2
- POST /destroy - Destroy sandbox (data survives!)
Try this flow:
1. POST /process (processes and saves to R2)
2. POST /destroy (destroys sandbox)
3. GET /results (data still accessible from R2)
`, { headers: { 'Content-Type': 'text/plain' } });
}
};4. Nasaďte do produkce
Vygenerujte tokeny R2 API:
- Přejděte na R2 > Přehled v Cloudflare dashboard ↗
- Vyberte Správa tokenů R2 API
- Vytvořte token s Object Read & Write oprávnění
- Zkopírujte Access Key ID a Secret Access Key
Nastavte přihlašovací údaje jako tajné klíče Workeru:
npx wrangler secret put AWS_ACCESS_KEY_ID
# Paste your R2 Access Key ID
npx wrangler secret put AWS_SECRET_ACCESS_KEY
# Paste your R2 Secret Access KeyTajné klíče Workeru jsou šifrované a přístupné pouze vašemu nasazenému Workeru. SDK tyto přihlašovací údaje automaticky rozpozná, když mountBucket() je volána.
Nasaďte svůj Worker:
npx wrangler deployPo nasazení wrangler vypíše URL vašeho Workeru (např. https://data-pipeline.yourname.workers.dev).
5. Otestujte průběh perzistence
Nyní otestujte proti nasazenému Workeru. Nahraďte YOUR_WORKER_URL skutečnou URL adresou vašeho Workeru:
# 1. Process data (saves to R2)
curl -X POST https://YOUR_WORKER_URL/process
# Returns: { "message": "Data processed...", "result": { "total": 144, "average": 48, ... } }
# 2. Verify data is accessible
curl https://YOUR_WORKER_URL/results
# Returns the same results from R2
# 3. Destroy the sandbox
curl -X POST https://YOUR_WORKER_URL/destroy
# Returns: { "message": "Sandbox destroyed. Data persists in R2!" }
# 4. Access results again (from new sandbox)
curl https://YOUR_WORKER_URL/results
# Still works! Data persisted across sandbox lifecycleKlíčové zjištění: po zničení sandboxu vytvoří další požadavek novou instanci sandboxu, připojí stejný R2 bucket a data v něm stále najde.
Co jste se naučili
V tomto tutoriálu jste vytvořili datový pipeline, který demonstruje perzistenci souborového systému pomocí připojení R2 bucketu:
- Připojování bucketů: Použijte
mountBucket()aby byl R2 dostupný jako místní adresář - Standardní operace se soubory: Přistupujte k připojeným bucketům pomocí známých příkazů souborového systému (
cat, Pythonopen(), atd.) - Automatická perzistence: Data zapsaná do připojených adresářů přežijí zničení sandboxu
- Vyberte správný model perzistence: Pro externí úložné adresáře použijte připojení bucketů, například
/data, a zvažte zálohování a obnovu, pokud potřebujete trvalý pracovní prostor pod/workspace - Správa přihlašovacích údajů: Nakonfigurujte přístup k R2 pomocí proměnných prostředí nebo explicitních přihlašovacích údajů
Další kroky
- Průvodce připojováním bucketů - Podrobná referenční příručka k připojování
- Storage API - Kompletní dokumentace API
- Proměnné prostředí - Možnosti konfigurace přihlašovacích údajů
Související zdroje
- Dokumentace R2 - Více o Cloudflare R2
- Průvodce procesy na pozadí - Dlouhotrvající zpracování dat
- Koncept sandboxů - Pochopení životního cyklu sandboxu