You need to enable JavaScript to run this app. 如何连接两个tus服务器 实现tus-node-server到Cloudflare上传接口的流式转发
优惠活动
大模型
产品
解决方案
定价
更多

如何连接两个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 15:24:04