我要启动火山引擎rtc的StartVoiceChat

10 2026-03-20 23:21 2026-03-21 00:04

我要启动火山引擎rtc的StartVoiceChat,要求从配置文件中获取参数。使用node和docker-compose,网页上只有两个按钮以及启动成功与否的报错调试信息

 

下面给出一份最小可运行的“Node.js + Docker Compose”示例,满足:
  1. 读取你提供的 config.json(已放在项目根目录)
  2. 网页只有两个按钮:「启动」和「停止」
  3. 点击按钮后,前端拿到「成功/失败」以及火山返回的原始信息,方便调试
  4. 全部走本地一个容器,端口 3000

一、目录结构 . ├── docker-compose.yml ├── Dockerfile ├── package.json ├── server.js ├── config.json ← 你上传的那份 └── public └── index.html

二、关键代码
  1. package.json
JSON
 
复制
{
  "name": "volc-rtc-demo",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "axios": "^1.6.0",
    "express": "^4.18.2",
    "uuid": "^9.0.1"
  }
}
 
  1. server.js(Node 18+)
JavaScript
 
复制
import express from 'express';
import axios from 'axios';
import { readFileSync } from 'fs';
import crypto from 'crypto';
import { v4 as uuid } from 'uuid';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const cfg = JSON.parse(readFileSync('./config.json', 'utf8'));

const app = express();
app.use(express.json());
app.use(express.static('public'));

// 火山签名工具函数
function signReq(method, uri, query, headers, ak, sk, region, service) {
  const algorithm = 'AWS4-HMAC-SHA256';
  const now = new Date();
  const dateStr = now.toISOString().replace(/[:\-]|\.\d{3}/g, '');
  const dateYmd = dateStr.slice(0, 8);
  const credentialScope = `${dateYmd}/${region}/${service}/request`;

  // 1. CanonicalRequest
  const headerKeys = Object.keys(headers).sort();
  const signedHeaders = headerKeys.join(';');
  const canonicalHeaders = headerKeys.map(k => `${k}:${headers[k]}\n`).join('');
  const payloadHash = headers['x-content-sha256'] || 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
  const canonicalReq = [
    method.toUpperCase(),
    uri,
    query || '',
    canonicalHeaders,
    signedHeaders,
    payloadHash
  ].join('\n');

  // 2. StringToSign
  const stringToSign = [
    algorithm,
    dateStr,
    credentialScope,
    crypto.createHash('sha256').update(canonicalReq).digest('hex')
  ].join('\n');

  // 3. SigningKey
  const kDate = crypto.createHmac('sha256', `AWS4${sk}`).update(dateYmd).digest();
  const kRegion = crypto.createHmac('sha256', kDate).update(region).digest();
  const kService = crypto.createHmac('sha256', kRegion).update(service).digest();
  const signingKey = crypto.createHmac('sha256', kService).update('request').digest();

  const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
  headers.Authorization = `${algorithm} Credential=${ak}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
}

// 统一请求火山 OpenAPI
async function volcRequest(action, body) {
  const host = cfg.ak_sk.API_HOST;
  const region = cfg.ak_sk.REGION;
  const service = cfg.ak_sk.SERVICE;
  const ak = cfg.ak_sk.AK;
  const sk = cfg.ak_sk.SK;

  const query = `Action=${action}&Version=2022-06-01`;
  const uri = '/';
  const url = `https://${host}${uri}?${query}`;

  const headers = {
    'content-type': 'application/json',
    'x-content-sha256': crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex'),
    host
  };

  signReq('POST', uri, query, headers, ak, sk, region, service);

  return axios.post(url, body, { headers });
}

// 启动语音聊天
app.post('/api/start', async (req, res) => {
  try {
    const r = await volcRequest('StartVoiceChat', cfg.start_data);
    res.json({ ok: true, data: r.data });
  } catch (e) {
    res.json({ ok: false, error: e.response?.data || e.message });
  }
});

// 停止语音聊天
app.post('/api/stop', async (req, res) => {
  try {
    const r = await volcRequest('StopVoiceChat', cfg.stop_data);
    res.json({ ok: true, data: r.data });
  } catch (e) {
    res.json({ ok: false, error: e.response?.data || e.message });
  }
});

app.listen(3000, () => console.log('listen :3000'));
 
  1. public/index.html
HTML
 
预览
 
复制
<!doctype html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>VolcRTC 快速调试</title>
  <style>
    body{font-family:Arial,Helvetica,sans-serif;text-align:center;margin-top:100px}
    button{width:120px;height:40px;margin:10px;font-size:16px}
    #log{width:600px;margin:30px auto;text-align:left;border:1px solid #ccc;padding:10px;height:200px;overflow:auto}
  </style>
</head>
<body>
  <h2>火山引擎 RTC 语音聊天</h2>
  <button id="btnStart">启动</button>
  <button id="btnStop">停止</button>
  <div id="log"></div>

  <script>
    const log = (msg) => document.getElementById('log').innerHTML += `<div>${new Date().toLocaleTimeString()} ${msg}</div>`;
    const post = async (url) => {
      try {
        const r = await fetch(url, {method:'POST'});
        const j = await r.json();
        if(j.ok) log(`✅ 成功:${JSON.stringify(j.data)}`);
        else     log(`❌ 失败:${JSON.stringify(j.error)}`);
      } catch(e) {
        log(`❌ 网络异常:${e.message}`);
      }
    };
    document.getElementById('btnStart').onclick = () => post('/api/start');
    document.getElementById('btnStop').onclick  = () => post('/api/stop');
  </script>
</body>
</html>
 
  1. Dockerfile
dockerfile
 
复制
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm i
COPY . .
EXPOSE 3000
CMD ["npm","start"]
 
  1. docker-compose.yml
yaml
 
复制
version: "3.9"
services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - ./config.json:/app/config.json:ro   # 改配置无需重建镜像
 

三、运行
  1. 把上面 6 个文件放到同一目录,config.json 用你那份。
  2. 终端执行
    bash
     
  1. 复制
    docker compose up --build
     
  2. 浏览器打开 http://localhost:3000
    点击「启动」/「停止」,下方灰色调试框会立刻给出火山返回的 JSON 或错误详情。

四、常见坑
  • 若提示 “Unauthorized”:
    1. 确认 AK/SK 与控制台一致
    2. 检查本机时间(签名对时间敏感)
  • 若提示 “AppId 不存在”:
    把 config.json 里的 AppId 换成你在火山 RTC 控制台真实创建的
  • 想改语音音色/ASR 模型:
    直接改 config.json 里对应字段,保存后重启容器即可(因挂载了只读卷)。
 
 
 
 
 
 
 
 
 
 
 
目录结构给我再看一遍
 
 
编辑
 
复制
 
分享
 
项目根目录结构(完整再列一次):
plain
 
复制
.
├── docker-compose.yml
├── Dockerfile
├── package.json
├── server.js
├── config.json          ← 你上传的那份
└── public
    └── index.html
 
 
 
 
 
 
 
 
 
 
 
 
根目录起个什么名字
 
 
编辑
 
复制
 
分享
 
名字随意,只要一眼能认出这是“火山 RTC 一键 Demo”即可。下面给你几个直接可用的文件夹命名,挑一个顺眼就行:
  1. volc-rtc-quickstart
  2. volc-rtc-docker-demo
  3. rtc-volcengine-node
  4. volc-rtc-oneclick
示例(选 1):
plain
 
复制
volc-rtc-quickstart/
├── docker-compose.yml
├── Dockerfile
├── package.json
├── server.js
├── config.json
└── public/
    └── index.html

 

 

如何启动?是用docker-compose up -d吗?

是的,用 docker-compose up -d 启动,但因为你添加了新依赖 katex,需要先重新构建镜像。

完整启动步骤

bash
 
复制
# 1. 进入项目目录
cd /path/to/your/project

# 2. 停止旧容器(如果正在运行)
docker-compose down

# 3. 重新构建并启动(关键!安装 katex 依赖)
docker-compose up -d --build

# 4. 查看日志确认启动成功
docker-compose logs -f

# 5. 测试访问
curl http://localhost:3000/health
 

常用命令速查

表格
 
 
 
 
命令 作用
docker-compose up -d 后台启动(首次或配置变更后需加 --build
docker-compose up -d --build 重新构建镜像并启动(安装新依赖时用)
docker-compose down 停止并删除容器
docker-compose logs -f 实时查看日志
docker-compose ps 查看运行状态
docker-compose restart 重启服务(不改镜像)

 

然后浏览器访问:
  • http://localhost:3000/ → 原有首页
  • http://localhost:3000/latex → LaTeX 编辑器

如果报错

如果提示 katex 模块找不到,说明镜像没重建,强制重新构建:
bash
 
复制
# 强制无缓存重建
docker-compose build --no-cache
docker-compose up -d
 
现在就可以运行 docker-compose up -d --build 启动了!

 

 

 

 

 

 

全部评论

·