Node.js 集成百度语音
// ==================== backend-proxy.js ====================
// 安装依赖:npm install express cors node-fetch
// 运行:node backend-proxy.jsconst express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');const app = express();
const PORT = 3001;// 百度 API 配置
const BAIDU_CONFIG = {API_KEY: "YOUR_API_KEY", // 替换为你的 API KeySECRET_KEY: "YOUR_SECRET_KEY", // 替换为你的 Secret Key
};// 允许跨域
app.use(cors());
app.use(express.json());// Token 缓存
let cachedToken = null;
let tokenExpireTime = 0;// 获取百度 Access Token
async function getBaiduToken() {if (cachedToken && Date.now() < tokenExpireTime) {return cachedToken;}try {const response = await fetch(`https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${BAIDU_CONFIG.API_KEY}&client_secret=${BAIDU_CONFIG.SECRET_KEY}`,{ method: 'POST' });const data = await response.json();if (data.access_token) {cachedToken = data.access_token;tokenExpireTime = Date.now() + (data.expires_in - 60) * 1000;return cachedToken;} else {throw new Error('Failed to get token: ' + JSON.stringify(data));}} catch (error) {console.error('Token request error:', error);throw error;}
}// 代理接口:获取语音
app.post('/api/tts', async (req, res) => {try {const { text, lang = 'zh-CN' } = req.body;if (!text) {return res.status(400).json({ error: 'Text is required' });}// 获取 tokenconst token = await getBaiduToken();// 语音参数const voiceParams = {per: lang === 'zh-CN' ? 4 : 1, // 发音人spd: 5, // 语速pit: 5, // 音调vol: 5, // 音量};// 调用百度 TTS APIconst response = await fetch('https://tsn.baidu.com/text2audio', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded',},body: new URLSearchParams({tex: text,tok: token,cuid: 'web_client',ctp: 1,lan: lang === 'zh-CN' ? 'zh' : 'en',...voiceParams,}),});if (response.ok) {// 返回音频流const audioBuffer = await response.buffer();res.set('Content-Type', 'audio/mp3');res.send(audioBuffer);} else {const errorText = await response.text();res.status(500).json({ error: 'Baidu TTS error', details: errorText });}} catch (error) {console.error('TTS request error:', error);res.status(500).json({ error: error.message });}
});// 健康检查
app.get('/health', (req, res) => {res.json({ status: 'ok', timestamp: new Date().toISOString() });
});app.listen(PORT, () => {console.log(`✅ Baidu TTS Proxy running at http://localhost:${PORT}`);console.log(` API endpoint: http://localhost:${PORT}/api/tts`);
});
完成!现在给你完整的部署步骤:
📦 完整部署方案
1️⃣ 创建后端代理服务
创建文件 backend-proxy.js(代码已在上面的 artifact 中)
bash
# 初始化项目
npm init -y# 安装依赖
npm install express cors node-fetch# 修改 backend-proxy.js 中的 API_KEY 和 SECRET_KEY# 启动服务
node backend-proxy.js
2️⃣ 前端配置
在前端代码中,确保 PROXY_URL 指向你的后端:
javascript
const BAIDU_TTS_CONFIG = {PROXY_URL: "http://localhost:3001/api/tts", // 开发环境// 生产环境改为: "https://your-domain.com/api/tts"
};
```### 3️⃣ 工作流程
```
前端调用 speak()↓
尝试浏览器在线语音 (Google/Microsoft)↓
网络正常?
├─ 是 → ✅ 使用在线语音(不访问百度)
└─ 否 → 检测到失败(3秒超时)↓
尝试本地语音↓
有本地语音?
├─ 是 → ✅ 使用本地语音(不访问百度)
└─ 否 → 🎤 调用后端代理↓
后端代理 → 百度 API → 返回音频↓
前端播放音频 ✅
🚀 生产环境部署
选项 A:部署到 Vercel/Railway(推荐)
javascript
// vercel.json 或 railway.json
{"version": 2,"builds": [{"src": "backend-proxy.js","use": "@vercel/node"}],"routes": [{"src": "/(.*)","dest": "backend-proxy.js"}]
}
选项 B:使用 Serverless 函数
如果你使用的是 Next.js,可以创建 API 路由:
javascript
// pages/api/tts.js
export default async function handler(req, res) {// 后端代理逻辑
}
✅ 优势
- 无 CORS 问题 - 通过后端代理解决
- API 密钥安全 - 密钥保存在后端
- 完整降级机制 - 在线语音 → 本地语音 → 百度 TTS
- 网络正常时不影响 - 只在必要时才调用百度
🧪 测试
bash
# 测试后端代理
curl -X POST http://localhost:3001/api/tts \-H "Content-Type: application/json" \-d '{"text":"你好世界","lang":"zh-CN"}' \--output test.mp3# 播放测试
# macOS: open test.mp3
# Linux: xdg-open test.mp3
# Windows: start test.mp3
这样就完美解决了 CORS 问题,并且只在真正需要时才使用百度 TTS!
