将兼容 S3 的对象存储桶挂载为本地文件系统路径。使用标准文件操作访问对象存储。对于生产环境中的 Cloudflare R2,你也可以按 Worker R2 绑定名称挂载,使凭据保留在 Worker 运行时中。
要在生产环境中挂载 R2 存储桶而无需将凭据传入容器,请添加 R2 绑定,并从 Worker 入口点导出 ContainerProxy。
{
"$schema": "./node_modules/wrangler/config-schema.json",
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "my-r2-bucket"
}
]
}[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-r2-bucket"import { ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };import { ContainerProxy } from "@cloudflare/sandbox";
export { ContainerProxy };当你省略 endpoint 时,mountBucket() 的第一个参数必须是 Worker R2 绑定名称,例如 MY_BUCKET。
在需要以下情况时挂载兼容 S3 的存储桶:
- 持久数据 — 数据在沙箱销毁后仍然保留
- 大型数据集 — 无需下载即可处理数据
- 共享存储 — 多个沙箱访问同一数据
- 经济高效的持久化 — 比保持沙箱存活更便宜
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "data-processor");
// Mount R2 bucket by Worker binding name
await sandbox.mountBucket("MY_BUCKET", "/data");
// Access bucket with standard filesystem operations
await sandbox.exec("ls", { args: ["/data"] });
await sandbox.writeFile("/data/results.json", JSON.stringify(results));
// Use from Python
await sandbox.exec("python", {
args: [
"-c",
`
import pandas as pd
df = pd.read_csv('/data/input.csv')
df.describe().to_csv('/data/summary.csv')
`,
],
});import { getSandbox } from '@cloudflare/sandbox';
const sandbox = getSandbox(env.Sandbox, 'data-processor');
// Mount R2 bucket by Worker binding name
await sandbox.mountBucket('MY_BUCKET', '/data');
// Access bucket with standard filesystem operations
await sandbox.exec('ls', { args: ['/data'] });
await sandbox.writeFile('/data/results.json', JSON.stringify(results));
// Use from Python
await sandbox.exec('python', { args: ['-c', `
import pandas as pd
df = pd.read_csv('/data/input.csv')
df.describe().to_csv('/data/summary.csv')
`] });在此示例中,MY_BUCKET 是 wrangler.toml 中的绑定名称。它不必与存储桶在仪表板中的名称匹配,尽管许多项目会使用匹配的名称。
R2 绑定挂载不需要凭据。远程端点挂载仍支持 Cloudflare R2 与其他兼容 S3 的提供商,这些流程仍可使用自动凭据检测或显式凭据。
当你包含 endpoint 时,将凭据设置为 Worker 密钥,SDK 会自动检测它们:
npx wrangler secret put R2_ACCESS_KEY_ID
npx wrangler secret put R2_SECRET_ACCESS_KEY// Credentials automatically detected from environment for remote endpoint mounts
await sandbox.mountBucket("my-r2-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
});// Credentials automatically detected from environment for remote endpoint mounts
await sandbox.mountBucket('my-r2-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com'
});在需要时直接传递凭据:
await sandbox.mountBucket("my-r2-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-r2-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com',
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY
}
});当你使用显式凭据挂载时,s3fs 会将这些凭据写入容器磁盘上的密码文件。被攻破的容器进程可能读取并外泄凭据,或用它们访问预期存储桶范围之外的存储。
设置 credentialProxy: true 可将凭据完全保留在容器之外。SDK 不会将真实凭据传入容器,而是由 Durable Object 在网络层拦截所有出站 S3 请求,用真实凭据重新签名,并转发到上游。容器仅持有在代理之外毫无用处的虚拟凭据。
这适用于兼容 S3 端点(包括 R2)的 AWS SigV4 ↗ 签名,以及 Google Cloud Storage 的 HMAC 签名。建议为所有端点挂载设置 credentialProxy: true。为保持向后兼容,该选项默认为 false,并将在未来版本的 Sandbox SDK 中成为默认值。
await sandbox.mountBucket("my-bucket", "/data", {
endpoint: "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
provider: "r2",
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
},
credentialProxy: true,
});await sandbox.mountBucket('my-bucket', '/data', {
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com',
provider: 'r2',
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY
},
credentialProxy: true
});使用 prefix 选项挂载存储桶中的特定子目录。仅前缀下的内容在挂载点可见:
// Mount only the /uploads/images/ subdirectory
await sandbox.mountBucket("MY_BUCKET", "/images", {
prefix: "/uploads/images/",
});
// Files appear at mount point without the prefix
// Bound bucket: my-r2-bucket/uploads/images/photo.jpg
// Mounted path: /images/photo.jpg
await sandbox.exec("ls", { args: ["/images"] });
// Write to subdirectory
await sandbox.writeFile("/images/photo.jpg", imageData);
// Creates my-r2-bucket/uploads/images/photo.jpg
// Mount different prefixes to different paths
await sandbox.mountBucket("MY_BUCKET", "/training-data", {
prefix: "/ml/training/",
});
await sandbox.mountBucket("MY_BUCKET", "/test-data", {
prefix: "/ml/testing/",
});// Mount only the /uploads/images/ subdirectory
await sandbox.mountBucket('MY_BUCKET', '/images', {
prefix: '/uploads/images/'
});
// Files appear at mount point without the prefix
// Bound bucket: my-r2-bucket/uploads/images/photo.jpg
// Mounted path: /images/photo.jpg
await sandbox.exec('ls', { args: ['/images'] });
// Write to subdirectory
await sandbox.writeFile('/images/photo.jpg', imageData);
// Creates my-r2-bucket/uploads/images/photo.jpg
// Mount different prefixes to different paths
await sandbox.mountBucket('MY_BUCKET', '/training-data', {
prefix: '/ml/training/'
});
await sandbox.mountBucket('MY_BUCKET', '/test-data', {
prefix: '/ml/testing/'
});通过以只读模式挂载存储桶来保护数据:
await sandbox.mountBucket("MY_BUCKET", "/data", {
readOnly: true,
});
// Reads work
await sandbox.exec("cat", { args: ["/data/dataset.csv"] });
// Writes fail
await sandbox.writeFile("/data/new-file.txt", "data"); // Error: Read-only filesystemawait sandbox.mountBucket('MY_BUCKET', '/data', {
readOnly: true
});
// Reads work
await sandbox.exec('cat', { args: ['/data/dataset.csv'] });
// Writes fail
await sandbox.writeFile('/data/new-file.txt', 'data'); // Error: Read-only filesystem你也可以在使用 wrangler dev 进行本地开发时,通过传递 localBucket 选项挂载 R2 存储桶。生产环境中的 R2 绑定挂载与本地 localBucket 挂载都避免使用显式凭据,但它们是不同的执行路径。生产使用无凭据的出站拦截并覆盖目标路径。本地开发使用与 R2 绑定的周期性同步。
await sandbox.mountBucket("MY_BUCKET", "/data", {
localBucket: true,
});
// Access files using standard operations
await sandbox.exec("ls", { args: ["/data"] });
await sandbox.writeFile("/data/results.json", JSON.stringify(results));await sandbox.mountBucket('MY_BUCKET', '/data', {
localBucket: true
});
// Access files using standard operations
await sandbox.exec('ls', { args: ['/data'] });
await sandbox.writeFile('/data/results.json', JSON.stringify(results));readOnly 与 prefix 选项在本地模式下的工作方式相同:
// Read-only local mount
await sandbox.mountBucket("MY_BUCKET", "/data", {
localBucket: true,
readOnly: true,
});
// Mount a subdirectory
await sandbox.mountBucket("MY_BUCKET", "/images", {
localBucket: true,
prefix: "/uploads/images/",
});// Read-only local mount
await sandbox.mountBucket('MY_BUCKET', '/data', {
localBucket: true,
readOnly: true
});
// Mount a subdirectory
await sandbox.mountBucket('MY_BUCKET', '/images', {
localBucket: true,
prefix: '/uploads/images/'
});在本地开发期间,文件通过周期性同步过程在 R2 与容器之间同步,而不是通过直接文件系统挂载。请注意以下几点:
- 同步窗口 — 文件写入与在另一侧出现之间存在短暂延迟。例如,如果你将文件上传到 R2,然后立即从容器中的挂载路径读取它,该文件可能尚不可用。在读取最近写入的数据之前,请留出短暂窗口以完成同步。
- 高频写入 — 对同一文件路径的快速连续写入可能需要稍长时间才能完全传播。为获得最佳结果,请避免同时从 R2 与容器写入同一文件。
- 双向同步 — 在容器中所做的更改会同步到 R2,在 R2 中所做的更改会同步到容器。两个方向都遵循相同的周期性同步模型。
// Mount for processing
await sandbox.mountBucket("MY_BUCKET", "/data");
// Do work
await sandbox.exec("python process_data.py");
// Clean up
await sandbox.unmountBucket("/data");// Mount for processing
await sandbox.mountBucket('MY_BUCKET', '/data');
// Do work
await sandbox.exec('python process_data.py');
// Clean up
await sandbox.unmountBucket('/data');SDK 支持任何兼容 S3 的对象存储。以下是常见提供商的示例:
await sandbox.mountBucket("my-s3-bucket", "/data", {
endpoint: "https://s3.us-west-2.amazonaws.com",
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-s3-bucket', '/data', {
endpoint: 'https://s3.us-west-2.amazonaws.com',
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
}
});await sandbox.mountBucket("my-gcs-bucket", "/data", {
endpoint: "https://storage.googleapis.com",
credentials: {
accessKeyId: env.GCS_ACCESS_KEY_ID,
secretAccessKey: env.GCS_SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-gcs-bucket', '/data', {
endpoint: 'https://storage.googleapis.com',
credentials: {
accessKeyId: env.GCS_ACCESS_KEY_ID,
secretAccessKey: env.GCS_SECRET_ACCESS_KEY
}
});对于 Backblaze B2、MinIO、Wasabi 或其他提供商,使用标准挂载模式:
await sandbox.mountBucket("my-bucket", "/data", {
endpoint: "https://s3.us-west-000.backblazeb2.com",
credentials: {
accessKeyId: env.ACCESS_KEY_ID,
secretAccessKey: env.SECRET_ACCESS_KEY,
},
});await sandbox.mountBucket('my-bucket', '/data', {
endpoint: 'https://s3.us-west-000.backblazeb2.com',
credentials: {
accessKeyId: env.ACCESS_KEY_ID,
secretAccessKey: env.SECRET_ACCESS_KEY
}
});有关特定提供商的配置,请参阅 s3fs-fuse wiki ↗ 了解受支持的提供商与推荐标志。
错误:R2 binding "MY_BUCKET" not found in Worker env
解决方案:确保你的 Worker 有 r2_buckets 绑定,且 mountBucket() 使用绑定名称,而不是存储桶在仪表板中的名称:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "my-r2-bucket"
}
]
}[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-r2-bucket"解决方案:确保你的 Worker 入口点导出 ContainerProxy。如果使用较旧的 Wrangler 版本,你可能还需要 enable_ctx_exports 兼容性标志。
错误:MissingCredentialsError: No credentials found
解决方案:此错误仅在你通过设置 endpoint 挂载远程兼容 S3 端点时适用。将凭据设置为 Worker 密钥:
npx wrangler secret put R2_ACCESS_KEY_ID
npx wrangler secret put R2_SECRET_ACCESS_KEY或
npx wrangler secret put AWS_ACCESS_KEY_ID
npx wrangler secret put AWS_SECRET_ACCESS_KEY错误:S3FSMountError: mount failed
常见原因:
- 端点 URL 不正确
- 凭据无效
- 缺少
ContainerProxy导出,或在较旧 Wrangler 版本上缺少enable_ctx_exports - 存储桶不存在
- 网络连接问题
验证你的绑定或端点配置:
try {
await sandbox.mountBucket("MY_BUCKET", "/data");
} catch (error) {
console.error("Mount failed:", error.message);
// Check binding name, ContainerProxy export, or remote endpoint configuration
}try {
await sandbox.mountBucket('MY_BUCKET', '/data');
} catch (error) {
console.error('Mount failed:', error.message);
// Check binding name, ContainerProxy export, or remote endpoint configuration
}错误:InvalidMountConfigError: Mount path already in use
解决方案:先卸载,或使用不同路径:
// Unmount existing
await sandbox.unmountBucket("/data");
// Or use different path
await sandbox.mountBucket("bucket2", "/storage", { endpoint: "..." });// Unmount existing
await sandbox.unmountBucket('/data');
// Or use different path
await sandbox.mountBucket('bucket2', '/storage', { endpoint: '...' });由于网络延迟,挂载存储桶上的文件操作比本地文件系统慢。
解决方案:将频繁访问的文件复制到本地:
// Copy to local filesystem
await sandbox.exec("cp", {
args: ["/data/large-dataset.csv", "/workspace/dataset.csv"],
});
// Work with local copy (faster)
await sandbox.exec("python", {
args: ["process.py", "/workspace/dataset.csv"],
});
// Save results back to bucket
await sandbox.exec("cp", {
args: ["/workspace/results.json", "/data/results/output.json"],
});// Copy to local filesystem
await sandbox.exec('cp', { args: ['/data/large-dataset.csv', '/workspace/dataset.csv'] });
// Work with local copy (faster)
await sandbox.exec('python', { args: ['process.py', '/workspace/dataset.csv'] });
// Save results back to bucket
await sandbox.exec('cp', { args: ['/workspace/results.json', '/data/results/output.json'] });- 尽早挂载 — 在沙箱初始化时挂载存储桶
- 选择正确的挂载模式 — 需要由 Worker 管理的 R2 访问时使用 R2 绑定挂载;对显式 R2、S3、GCS 及其他兼容 S3 的提供商使用
endpoint - 保护凭据 — 始终使用 Worker 密钥,切勿硬编码
- 尽可能只读 — 使用只读挂载保护数据
- 挂载最窄路径 — 使用前缀仅暴露沙箱所需的数据
- 挂载路径 — 优先使用
/data、/storage或/mnt/*;如果挂载在/workspace下,请考虑生产中挂载会覆盖该路径 - 处理错误 — 在
try...catch块中包装挂载操作 - 优化访问 — 将频繁访问的文件复制到本地