eisonme-node-transfer
OpenClaw 节点间基于 Node.js 流的高速大文件传输,避免 Base64 与 OOM。
安装 / 下载方式
TotalClaw CLI推荐
totalclaw skills install shop:totalclaw:totalclaw~eisonme-node-transfercURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~eisonme-node-transfer/file -o eisonme-node-transfer.md## 概述(中文)
OpenClaw 节点间基于 Node.js 流的高速大文件传输,避免 Base64 与 OOM。
## 技能正文
# node-transfer(节点传输)
使用原生 Node.js 流在 OpenClaw 节点间进行高速、内存高效的文件传输。
## 📋 目录
- [解决的问题](#problem-solved)
- [架构](#architecture)
- [要求](#requirements)
- [安装](#installation)
- [用法](#usage)
- [API 参考](#api-reference)
- [故障排除](#troubleshooting)
---
## 🎯 解决的问题
### 原始问题
使用标准 `nodes.invoke` 机制在 OpenClaw 节点间传输大文件时,我们遇到几个关键问题:
| 问题 | 影响 |
|-------|--------|
| **Base64 编码开销** | 载荷大 33%,传输更慢 |
| **内存耗尽 (OOM)** | 将数 GB 文件载入内存导致进程崩溃 |
| **传输延迟** | JSON 序列化/反序列化增加显著延迟 |
| **9 分钟部署** | 每次传输都重新部署脚本 |
### 解决方案
`node-transfer` 使用 **原生 HTTP 流**与 Node.js 流,提供:
- ✅ **零内存开销** — 文件从磁盘直接流到网络
- ✅ **无 Base64 编码** — 原始二进制传输
- ✅ **速度** — 仅受网络带宽限制的线速
- ✅ **一次安装,多次运行** — 首次部署后脚本持久保留在节点
### 性能对比
| 指标 | Base64 传输 | node-transfer | 提升 |
|--------|----------------|---------------|-------------|
| 1GB 文件传输时间 | ~15-30 分钟 | ~8 秒 | **约 150 倍** |
| 内存使用 | 1GB+ | <10MB | **减少 99%** |
| 首次传输开销 | N/A | ~30 秒(一次性安装) | - |
| 后续传输 | ~15-30 分钟 | **<1 秒** 检查 + ~8 秒传输 | **约 200 倍** |
---
## 🏗️ 架构
### 工作原理
```
┌──────────────┐ HTTP Stream ┌──────────────┐
│ send.js │ ◄──────────────────► │ receive.js │
│ (Source) │ (Token-protected) │ (Destination)│
└──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Read Stream │ │ Write Stream │
│ (fs.create │ │ (fs.create │
│ ReadStream)│ │ WriteStream)│
└──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ File on │ │ File on │
│ Disk │ │ Disk │
└──────────────┘ └──────────────┘
```
### 安全模型
1. **一次性令牌**:256 位加密随机令牌(64 个十六进制字符)
2. **单次连接**:每个令牌仅允许一次下载
3. **自动关闭**:传输完成或断开后服务器关闭
4. **令牌验证**:每个请求必须包含正确令牌
### 数据流
1. **发送方**(`send.js`):
- 生成随机端口与安全令牌
- 在临时端口启动 HTTP 服务器
- 将文件从磁盘直接流到 HTTP 响应
- 传输或超时后自动关闭(默认 5 分钟)
2. **接收方**(`receive.js`):
- 使用令牌连接发送方 URL
- 将 HTTP 响应直接流到磁盘
- 报告进度、速度与完成状态
- 验证接收字节与预期大小一致
---
## 📦 要求
- **Node.js**:14.0.0 或更高
- **网络**:节点间 TCP 连通(任意端口 1024-65535)
- **防火墙**:必须允许出站连接与临时端口入站
- **磁盘空间**:目标端有足够空间存放接收文件
---
## 🚀 安装
### 「一次安装」模式
不在每次传输时部署脚本,而是**每节点部署一次**,后续传输使用快速版本检查。
### 方法 1:使用 deploy.js(推荐)
```bash
# Generate deployment script for a target node
node deploy.js E3V3
# This outputs a PowerShell script that you can execute via nodes.invoke()
```
### 方法 2:手动部署
在每个目标节点创建目录并复制文件:
```powershell
# Create directory
mkdir C:/openclaw/skills/node-transfer/scripts -Force
# Copy these files (ensure UTF-8 without BOM encoding):
# - send.js
# - receive.js
# - ensure-installed.js
# - version.js
```
### 方法 3:通过 OpenClaw 智能体
```javascript
// 1. Check if already installed (< 100ms)
const check = await nodes.invoke({
node: 'E3V3',
command: ['node', 'C:/openclaw/skills/node-transfer/scripts/ensure-installed.js',
'C:/openclaw/skills/node-transfer/scripts']
});
const checkResult = JSON.parse(check.output);
if (!checkResult.installed) {
// 2. Deploy if needed (one-time, ~30 seconds)
// Use the deploy.js output or manually copy files
console.log('Deploying node-transfer to E3V3...');
// ... deployment code ...
}
```
---
## 💡 用法
### 基本传输工作流
```javascript
const INSTALL_DIR = 'C:/openclaw/skills/node-transfer/scripts';
const SOURCE_NODE = 'E3V3';
const DEST_NODE = 'E3V3-Docker';
// Step 1: Check installation on both nodes (fast!)
const [sourceCheck, destCheck] = await Promise.all([
nodes.invoke({
node: SOURCE_NODE,
command: ['node', `${INSTALL_DIR}/ensure-installed.js`, INSTALL_DIR]
}),
nodes.invoke({
node: DEST_NODE,
command: ['node', `${INSTALL_DIR}/ensure-installed.js`, INSTALL_DIR]
})
]);
// Deploy if needed (usually only once per node ever)
// ... deployment code if not installed ...
// Step 2: Start sender on source node
const sendResult = await nodes.invoke({
node: SOURCE_NODE,
command: ['node', `${INSTALL_DIR}/send.js`, 'C:/data/large-file.zip']
});
const { url, token, fileSize, fileName } = JSON.parse(sendResult.output);
// Step 3: Start receiver on destination node
const receiveResult = await nodes.invoke({
node: DEST_NODE,
command: ['node', `${INSTALL_DIR}/receive.js`, url, token, '/incoming/file.zip']
});
const result = JSON.parse(receiveResult.output);
console.log(`Transferred ${result.bytesReceived} bytes in ${result.duration}s at ${result.speedMBps} MB/s`);
```
### 使用命令行
#### 发送方
```bash
node send.js /path/to/file.zip
```
输出:
```json
{
"url": "http://192.168.1.10:54321/transfer",
"token": "a1b2c3d4e5f6789...",
"fileSize": 1073741824,
"fileName": "file.zip",
"sourceIp": "192.168.1.10",
"port": 54321,
"version": "1.0.0"
}
```
选项:
```bash
node send.js /path/to/file.zip --port 8080 --timeout 10
node send.js --help
node send.js --version
```
#### 接收方
```bash
node receive.js "http://192.168.1.10:54321/transfer" "token-here..." /path/to/save.zip
```
输出:
```json
{
"success": true,
"bytesReceived": 1073741824,
"totalBytes": 1073741824,
"duration": 8.42,
"speedMBps": 121.5,
"outputPath": "/path/to/save.zip"
}
```
选项:
```bash
node receive.js <url> <token> <output> --timeout 60 --no-progress
node receive.js --help
node receive.js --version
```
---
## 📚 API 参考
### send.js
启动 HTTP 服务器以流式传输文件。
**用法:** `node send.js <filePath> [options]`
**参数:**
- `filePath`(必需):要发送的文件路径
**选项:**
- `--port <n>`:使用指定端口(默认:随机临时端口)
- `--timeout <n>`:超时分钟数(默认:5)
**输出 (JSON):**
| 字段 | 类型 | 说明 |
|-------|------|-------------|
| `url` | string | 接收方连接的 HTTP URL |
| `token` | string | 安全令牌(64 个十六进制字符) |
| `fileSize` | number | 文件大小(字节) |
| `fileName` | string | 原始文件名 |
| `sourceIp` | string | 发送方 IP 地址 |
| `port` | number | 使用的 TCP 端口 |
| `version` | string | send.js 版本 |
**退出码:**
- `0`:成功(传输完成或信息显示)
- `1`:错误(查看 stderr 的 JSON 错误详情)
**错误输出 (JSON):**
```json
{
"error": "ERROR_CODE",
"message": "Human-readable description"
}
```
错误码:`FILE_NOT_FOUND`、`NOT_A_FILE`、`SERVER_ERROR`、`TIMEOUT`、`READ_ERROR`、`RESPONSE_ERROR`
---
### receive.js
连接发送方并下载文件。
**用法:** `node receive.js <url> <token> <outputPath> [options]`
**参数:**
- `url`(必需):send.js 输出的 URL
- `token`(必需):send.js 输出的安全令牌
- `outputPath`(必需):保存接收文件的路径
**选项:**
- `--timeout <n>`:连接超时秒数(默认:30)
- `--no-progress`:抑制进度更新
**输出 (JSON):**
| 字段 | 类型 | 说明 |
|-------|------|-------------|
| `success` | boolean | 成功时恒为 true |
| `bytesReceived` | number | 实际接收字节 |
| `totalBytes` | number | 预期字节(来自 Content-Length) |
| `duration` | number | 传输时间(秒) |
| `speedMBps` | number | 平均速度 MB/s |
| `outputPath` | string | 保存文件的绝对路径 |
**进度更新(未使用 `--no-progress` 时):**
```json
{
"progress": true,
"receivedBytes": 536870912,
"totalBytes": 1073741824,
"percent": 50,
"speedMBps": 125.4
}
```
**退出码:**
- `0`: Success
- `1`:错误(查看 stderr 的 JSON 错误详情)
Error codes: `INVALID_ARGS`, `INVALID_URL`, `CONNECTION_ERROR`, `HTTP_ERROR`, `TIMEOUT`, `WRITE_ERROR`, `SIZE_MISMATCH`, `FILE_EXISTS`, `NO_DATA`
---
### ensure-installed.js
快速检查节点是否已安装 node-transfer。
**用法:** `node ensure-installed.js <targetDir>`
**参数:**
- `targetDir`(必需):要检查的目录
**输出 (JSON):**
已安装:
```json
{
"installed": true,
"version": "1.0.0",
"message": "node-transfer is installed and up-to-date"
}
```
需要安装:
```json
{
"installed": false,
"missing": ["send.js"],
"mismatched": [],
"currentVersion": null,
"requiredVersion": "1.0.0",
"action": "DEPLOY",
"message": "Installation needed: 1 missing, 0 outdated"
}
```
**退出码:**
- `0`:已安装且为最新
- `1`:需要安装/更新
- `2`:错误(无效目录等)
---
### deploy.js
为主智能体生成部署脚本。
**用法:** `node deploy.js <nodeId> [targetDir]`
**输出:** JSON with:
- `script`:部署文件的 PowerShell 脚本
- `escapedScript`:命令