如何连接两个tus服务器 实现tus-node-server到Cloudflare上传接口的流式转发
实现tus-node-server流式转发到Cloudflare tus接口方案
完全可以实现,无需将完整文件缓存到自有服务器,通过自定义tus存储适配器即可实现数据流的直接透传,全程不会落盘也不会在服务端留存完整文件。
核心实现逻辑
tus-node-server支持自定义存储后端,不需要使用默认的本地文件存储方案。你只需重写核心的存储操作方法,每收到前端上传的数据流块,直接同步转发给Cloudflare的tus接口即可,全程仅持有当前传输块的内存占用。
具体实现步骤
- 自定义tus存储适配器,继承
@tus/server提供的DataStore基类,重写create、write、getOffset三个核心方法,所有操作直接对接Cloudflare的tus接口 write方法中直接将前端传来的请求流作为转发请求的body,通过流式HTTP请求库透传到Cloudflare,不需要读取完整文件内容到内存- Cloudflare的访问凭证全部在服务端注入到转发请求的头中,前端全程不会接触到密钥,避免凭证泄露
- 所有Cloudflare返回的响应头、状态码直接透传回前端,保证前端tus客户端可以正常识别上传进度、断点状态和错误信息
最小实现代码示例
const { DataStore } = require('@tus/server'); const { fetch } = require('undici'); const express = require('express'); // 自定义Cloudflare R2 tus存储适配器 class CloudflareTusStore extends DataStore { constructor(options) { super(); this.cfAccountId = options.cfAccountId; this.cfApiToken = options.cfApiToken; this.cfBucket = options.cfBucket; this.cfBaseUrl = `https://${this.cfAccountId}.r2.cloudflarestorage.com/${this.cfBucket}`; } // 创建上传会话 async create(req) { const cfRes = await fetch(this.cfBaseUrl, { method: 'POST', headers: { ...req.headers, Authorization: `Bearer ${this.cfApiToken}`, Host: `${this.cfAccountId}.r2.cloudflarestorage.com` }, body: req.body }); return { upload_id: cfRes.headers.get('upload-id'), size: parseInt(req.headers['upload-length']), offset: 0 }; } // 写入上传块,直接流式转发 async write(req, uploadId, offset) { const cfRes = await fetch(`${this.cfBaseUrl}/${uploadId}`, { method: 'PATCH', headers: { ...req.headers, Authorization: `Bearer ${this.cfApiToken}`, Host: `${this.cfAccountId}.r2.cloudflarestorage.com`, 'Upload-Offset': offset.toString() }, body: req.body }); return parseInt(cfRes.headers.get('upload-offset')); } // 查询上传进度,对接Cloudflare断点状态 async getOffset(uploadId) { const cfRes = await fetch(`${this.cfBaseUrl}/${uploadId}`, { method: 'HEAD', headers: { Authorization: `Bearer ${this.cfApiToken}`, Host: `${this.cfAccountId}.r2.cloudflarestorage.com` } }); return { offset: parseInt(cfRes.headers.get('upload-offset')), size: parseInt(cfRes.headers.get('upload-length')) }; } } // 注册到express服务 const app = express(); const tusServer = new TusServer({ path: '/upload', datastore: new CloudflareTusStore({ cfAccountId: process.env.CF_ACCOUNT_ID, cfApiToken: process.env.CF_API_TOKEN, cfBucket: process.env.CF_BUCKET_NAME }) }); app.use('/upload', (req, res) => { tusServer.handle(req, res); }); app.listen(3000, () => console.log('服务启动在3000端口'));
注意事项
- 可通过调整流式请求的
highWaterMark参数控制单次转发的块大小,避免大文件传输时内存溢出 - 部分tus扩展头需要按照Cloudflare的要求做适配,不要随意修改tus协议相关的请求头参数
- 你可以在适配器中添加自定义的权限校验逻辑,在转发请求前先验证前端的上传权限
内容的提问来源于stack exchange,提问作者Ehsan Shekari
相关产品推荐
相关产品推荐

