跳转到内容
搜索文档

挂载存储桶

最后更新 查看 MarkdownAgent 设置

将兼容 S3 的对象存储桶挂载为本地文件系统路径。使用标准文件操作访问对象存储。对于生产环境中的 Cloudflare R2,你也可以按 Worker R2 绑定名称挂载,使凭据保留在 Worker 运行时中。

R2 绑定挂载的生产前提条件

要在生产环境中挂载 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 的存储桶:

  • 持久数据 — 数据在沙箱销毁后仍然保留
  • 大型数据集 — 无需下载即可处理数据
  • 共享存储 — 多个沙箱访问同一数据
  • 经济高效的持久化 — 比保持沙箱存活更便宜

挂载 R2 存储桶

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_BUCKETwrangler.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 filesystem
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 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));

readOnlyprefix 选项在本地模式下的工作方式相同:

// 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 的对象存储。以下是常见提供商的示例:

Amazon 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
	}
});

Google Cloud Storage

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
	}
});

其他兼容 S3 的提供商

对于 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 绑定未找到错误

错误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"

无凭据 R2 挂载立即失败

解决方案:确保你的 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 块中包装挂载操作
  • 优化访问 — 将频繁访问的文件复制到本地

相关资源

这篇文档对您有帮助吗?