news 2026/9/15 14:11:06

Cloudflare 临时邮箱 S3 附件存储配置指南:R2 对象存储接入与附件管理实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cloudflare 临时邮箱 S3 附件存储配置指南:R2 对象存储接入与附件管理实战

Cloudflare 临时邮箱 S3 附件存储配置指南:R2 对象存储接入与附件管理实战

【免费下载链接】cloudflare_temp_emailCloudFlare free temp domain email 免费收发 临时域名邮箱 支持附件 IMAP SMTP TelegramBot项目地址: https://gitcode.com/GitHub_Trending/cl/cloudflare_temp_email

本指南以 cloudflare_temp_email 项目的 S3 附件功能为核心,讲解如何在 Cloudflare R2(或其他兼容 S3 协议的对象存储)上保存与下载邮件附件,涵盖 bucket 与凭证创建、Worker Secrets 配置、签名 URL 上传下载流程,以及前端交互与后端 API 的完整实现链路。读完本文,你将能独立为本项目启用附件持久化存储,并理解其底层签名 URL 机制与密钥管理方式。

说明:以上截图来自仓库文档 vitepress-docs/docs/zh/guide/feature/s3-attachment.md,展示在邮件详情中查看附件并将附件保存到 S3 的界面操作。

一、功能定位:为什么需要 S3 附件

在临时邮箱场景中,邮件正文通常存储在 Cloudflare D1 数据库里,而邮件附件的体积往往远超文本内容。若将所有附件一并写入 D1,会带来两个问题:

  • 数据库体积膨胀:大量 Base64 编码的附件会迅速消耗 D1 的存储配额,且 D1 按读取行数与存储量计费,成本随之上升;
  • 数据库读写效率下降:列表查询、全文检索等操作会因行内的大字段而变慢。

因此项目将附件"外置"到对象存储:正文留在 D1,附件按需上传到 S3 兼容存储(官方文档直接以 Cloudflare R2 为例,也兼容其他 S3 服务)。附件与邮件分离后,邮件列表页无需加载附件二进制数据,只有用户主动保存或下载时才通过签名 URL 与对象存储交互。

[!NOTE] 此功能是可选项。若你的使用场景不需要保存附件,可以直接跳过本章节,系统其余功能不受影响。

二、前置条件与配置项总览

启用 S3 附件功能前,需要准备:

  1. 一个 Cloudflare 账号,并在 Cloudflare 控制台创建R2 bucket(也可以使用其他兼容 S3 协议的对象存储服务,如遇兼容性问题可在项目仓库提 issue);
  2. 参考 Cloudflare 官方文档为 bucket 配置CORS 策略,允许来自你部署域名的跨域请求(前端需要直接向对象存储发起 PUT 请求上传附件);
  3. 参考 Cloudflare R2 的S3 Token文档创建访问令牌,获得以下三个凭证:
    • ENDPOINT(S3 兼容端点地址)
    • Access Key ID
    • Secret Access Key

这些凭证会被配置为 Cloudflare Worker 的 Secrets。从源码看,Worker 侧共使用四个与 S3 相关的环境变量,定义于 worker/src/types.d.ts:

环境变量类型作用
S3_ENDPOINTstring \| undefinedS3 兼容服务的端点地址(R2 的 S3 API 端点)
S3_ACCESS_KEY_IDstring \| undefinedR2 S3 Token 的 Access Key ID
S3_SECRET_ACCESS_KEYstring \| undefinedR2 S3 Token 的 Secret Access Key
S3_BUCKETstring \| undefined对象存储 bucket 名称
S3_URL_EXPIRESnumber \| undefined可选,签名 URL 有效期(秒),默认 360 秒

其中前四个变量是启用功能的必要条件。源码 worker/src/mails_api/s3_attachment.ts 中通过isS3Enabled判断四个变量是否全部非空,只有全部配置后才认为 S3 附件功能可用:

export const isS3Enabled = (c: Context<HonoCustomType>) => { return !(!c.env.S3_ENDPOINT || !c.env.S3_ACCESS_KEY_ID || !c.env.S3_SECRET_ACCESS_KEY || !c.env.S3_BUCKET); }

S3_URL_EXPIRES是可选配置,控制签名 URL 的有效期,源码中取值为c.env.S3_URL_EXPIRES || 360,即未配置时默认 360 秒。

三、配置步骤:创建 R2 Bucket 与写入 Worker Secrets

3.1 创建 bucket 并准备 CORS 与凭证

  1. 登录 Cloudflare 控制台,进入R2 → Buckets,创建一个 bucket(例如命名为temp-mail-attachments),记下 bucket 名称;
  2. 在该 bucket 的Settings → CORS中按 Cloudflare 官方文档添加 CORS 策略,允许你的前端域名通过浏览器跨域上传/下载附件(对应前端的PUTGET请求);
  3. 进入R2 → Manage API Tokens → Create API Token,按 R2 S3 Token 的方式生成凭证,页面会返回ENDPOINTAccess Key IDSecret Access Key三项信息。

3.2 写入 Worker Secrets

在项目根目录进入worker目录,逐条执行wrangler secret put命令将凭证写入 Worker 的 Secrets:

cd worker pnpm wrangler secret put S3_ENDPOINT pnpm wrangler secret put S3_ACCESS_KEY_ID pnpm wrangler secret put S3_SECRET_ACCESS_KEY # 请注意这里的 bucket 是你的 bucket 名称 pnpm wrangler secret put S3_BUCKET

每条命令执行后,终端会提示输入对应的值(粘贴凭证后回车即可)。wrangler secret put会将值加密存储,不会明文写入代码或配置文件。

[!NOTE] 除了命令行方式,也可以直接在 Cloudflare Worker 的 UI 界面中,通过Settings → Variables and Secrets添加同名 Secrets。

3.3 验证功能是否生效

配置完成后,S3 功能是否开启会通过 Worker 的两个接口暴露给前端:

  • worker/src/commom_api.ts 中的公共设置接口返回isS3Enabled布尔值;
  • worker/src/admin_api/worker_config.ts 中的 Worker 配置接口同样返回S3_ENABLED字段。

两者均直接复用isS3Enabled(c)的判定结果。前端拿到该值后,会决定是否展示"S3 附件"标签页以及邮件列表中的"保存到S3"按钮(见 frontend/src/views/Index.vue 中showSaveS3="openSettings.isS3Enabled"v-if="openSettings.isS3Enabled"的写法)。

四、使用方式:保存与下载附件

配置完成后,前端界面出现两个入口:

  • 保存附件:打开任意邮件的附件列表,点击"保存到S3"按钮,将当前附件上传至对象存储(见 vitepress-docs/docs/zh/guide/feature/s3-attachment.md 中的界面截图);
  • 下载附件:进入"S3附件"标签页,查看已保存的附件列表,点击"下载"即可取回文件。

说明:上图为 S3 附件管理页的界面截图(来源同仓库文档),表格中展示附件的 key(形如37/one-api.db)与下载操作按钮。

4.1 前端"保存到S3"完整流程

以 frontend/src/views/Index.vue 中的saveToS3为例,保存流程分两步:

  1. 向后端请求签名 PUT URL:
    const { url } = await api.fetch(`/api/attachment/put_url`, { method: 'POST', body: JSON.stringify({ key: `${mail_id}/${filename}` }) });

    注意这里前端把附件 key 组织为mail_id/文件名的形式;

  2. 拿到 URL 后,通过表单数据直接向该 URL 发起PUT请求上传:
    const formData = new FormData(); formData.append(filename, blob); await fetch(url, { method: 'PUT', body: formData }); message.success(t('saveToS3Success'));

    上传成功后提示"保存到s3成功"。

4.2 前端"S3附件"列表与下载

S3 附件列表页 对应后端listget_url两个接口:

  • 进入页面时调用GET /api/attachment/list拉取当前地址下的附件 key 列表;
  • 点击"下载"时调用POST /api/attachment/get_url换取签名 GET URL,再跳转下载。

删除附件则调用POST /api/attachment/delete

五、后端实现原理:签名 URL 与按地址隔离

所有附件 API 都集中在 worker/src/mails_api/s3_attachment.ts,并统一注册在 worker/src/mails_api/index.ts:

// attachment (S3) api.get('/api/attachment/list', s3_attachment.list) api.post('/api/attachment/delete', s3_attachment.deleteKey) api.post('/api/attachment/put_url', s3_attachment.getSignedPutUrl) api.post('/api/attachment/get_url', s3_attachment.getSignedGetUrl)

5.1 客户端初始化与密钥隔离

getS3Client使用@aws-sdk/client-s3构造 S3 客户端,region固定为"auto"(R2 的默认区域约定),端点与凭证来自上述环境变量:

return new S3Client({ region: "auto", endpoint: c.env.S3_ENDPOINT, credentials: { accessKeyId: c.env.S3_ACCESS_KEY_ID, secretAccessKey: c.env.S3_SECRET_ACCESS_KEY, }, });

值得注意的一个安全设计:所有对象的 key 都以当前登录邮箱地址作为前缀。例如列表接口只列出Prefix: ${address}/下的对象,删除、签名下载、签名上传也都拼接${address}/${key}。这意味着即使多个临时地址共用同一个 bucket,附件也按地址相互隔离,一个地址的用户无法列举或操作其他地址的附件。当前地址来自请求中的 JWT payload(c.get("jwtPayload")),因此该接口必须经过登录鉴权。

5.2 签名 URL:附件不经过 Worker 转发

四个接口中,上传与下载都采用AWS S3 预签名 URL(presigned URL)模式:

  • getSignedPutUrl:用PutObjectCommand生成 PUT 签名 URL,前端直接向对象存储上传;
  • getSignedGetUrl:用GetObjectCommand生成 GET 签名 URL,前端直接下载。

签名 URL 的有效期由S3_URL_EXPIRES控制(默认 360 秒),使用@aws-sdk/s3-request-presignergetSignedUrl生成。这种设计的核心收益是:附件二进制数据不经过 Worker 中转,Worker 只负责鉴权与签发有时效的 URL,既减轻了 Worker 的 CPU 与流量开销,也避免 D1 存储被附件撑大。

5.3 列表与删除

  • list:调用ListObjectsV2CommandPrefix限定为address/,返回结果中剔除地址前缀后输出{ key }数组;
  • deleteKey:调用DeleteObjectCommand删除指定 key 的对象,返回{ success: true }

六、附件裁剪策略:与 S3 的配合

为了控制邮件正文与附件的整体体积,Worker 还提供附件裁剪逻辑,位于 worker/src/email/check_attachment.ts:

  • 开启REMOVE_ALL_ATTACHMENT时,收信后直接移除全部附件;
  • 开启REMOVE_EXCEED_SIZE_ATTACHMENT且邮件大小 ≥ 2MB 时,移除超过阈值的附件。

被移除的附件在重新生成的 MIME 邮件中被清空(attachments: []),从而避免超大邮件写入 D1。结合 S3 附件功能,合理的策略是:正文与常规附件入库,超大或需要留存的附件保存到 R2,兼顾查询性能与存储成本。需要说明的是,这两项裁剪策略与 S3 保存是相互独立的开关,是否同时启用由你的业务诉求决定。

七、注意事项

  • CORS 必配:由于上传是前端浏览器直接向 R2 发起PUT请求,bucket 的 CORS 策略必须允许你的站点域名(含请求方法PUT/GET与所需请求头),否则浏览器会拦截跨域请求;
  • Secrets 不落盘S3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEY属于敏感凭证,请通过wrangler secret put或 Worker UI 的 Secrets 管理,不要硬编码到wrangler.toml或提交进仓库;
  • 签名 URL 有时效:上传/下载 URL 默认 360 秒内有效,过期后需重新向后端申请;
  • bucket 名称即配置值S3_BUCKET的值就是你在 R2 中创建的 bucket 名称,注意大小写与名称完全一致;
  • 其他 S3 服务:官方文档说明可以接入其他兼容 S3 协议的服务,但 R2 之外的兼容性问题请通过 issue 反馈,项目按 R2 的 S3 API 行为为主进行开发与测试。

至此,你已完整掌握该项目的 S3 附件配置、界面操作与底层实现。相关源码入口可继续查阅 worker/src/mails_api/s3_attachment.ts、worker/src/mails_api/index.ts 与 frontend/src/views/Index.vue。

【免费下载链接】cloudflare_temp_emailCloudFlare free temp domain email 免费收发 临时域名邮箱 支持附件 IMAP SMTP TelegramBot项目地址: https://gitcode.com/GitHub_Trending/cl/cloudflare_temp_email

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 14:09:34

K8s集群Helm仓库私有部署对接实操

K8s集群Helm仓库私有部署对接实操技术栈&#xff1a;Kubernetes v1.32.13 Rocky Linux 8.6 Containerd 1.7.x操作环境 / 对接原理 / 详细步骤 / 完整命令 / 配置文件 / 验证流程 / 排错方案K8s集群Helm仓库私有部署对接实操操作环境K8s 集群 3 节点&#xff1a;k8s-master(19…

作者头像 李华
网站建设 2026/9/15 14:09:34

React Native与鸿蒙跨平台健康数据管理实践

1. 跨平台健康数据管理的核心挑战在移动应用开发领域&#xff0c;健康数据管理一直是个特殊的存在。这类数据通常具有三个典型特征&#xff1a;高频更新&#xff08;如心率监测&#xff09;、多源异构&#xff08;来自不同传感器和设备&#xff09;以及强一致性要求&#xff08…

作者头像 李华
网站建设 2026/9/15 14:07:40

网页版贪吃蛇游戏:从Canvas绘制到游戏循环的完整实现

简介&#xff1a;一款基于HTML、CSS与JavaScript实现的网页版贪吃蛇游戏完整源码包&#xff0c;面向前端初学者、游戏开发爱好者以及需要完成课程设计的学生群体&#xff0c;能够解决从零搭建一个可运行贪吃蛇游戏的需求&#xff0c;体验完整的界面渲染、事件监听与游戏循环开发…

作者头像 李华
网站建设 2026/9/15 14:07:14

基于OpenCV的车牌号码识别Python代码:HSV定位与模板匹配实战

简介&#xff1a;面向计算机视觉课程设计与期末大作业场景&#xff0c;基于OpenCV的车牌号码识别项目提供了可直接运行的Python源码&#xff0c;适合需要快速搭建完整识别流程的本科学生&#xff0c;也适合作为项目实战练习的入门范例。代码覆盖车牌定位、字符分割、SVM分类识别…

作者头像 李华
网站建设 2026/9/15 14:07:07

Vue3工业MQTT实时监控系统设计与实践

简介&#xff1a;本资源是一个面向工业物联网场景的前端管理系统实战项目&#xff0c;专为中高级前端开发者及IoT系统工程师设计&#xff0c;解决工业设备实时监控、多源数据可视化与MQTT协议集成等核心问题。压缩包共262个文件&#xff0c;主体为96个Vue3单文件组件与140个Typ…

作者头像 李华