前端页面(html)
javascript
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阿里云语音合成演示</title>
<style>
/* 样式代码保持不变(见原始代码) */
</style>
</head>
<body>
<div class="container">
<!-- 页面结构保持不变(见原始代码) -->
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const synthesizeBtn = document.getElementById('synthesize');
const audioPlayer = document.getElementById('audioPlayer');
const statusDiv = document.getElementById('status');
synthesizeBtn.addEventListener('click', async () => {
// 获取输入参数
const params = {
appkey: document.getElementById('appkey').value.trim(),
token: document.getElementById('token').value.trim(),
text: document.getElementById('text').value.trim(),
voice: document.getElementById('voice').value,
volume: document.getElementById('volume').value,
speed: document.getElementById('speed').value
};
// 参数验证逻辑...
synthesizeBtn.disabled = true;
showStatus('正在合成语音,请稍候...', 'info');
try {
// 调用代理服务
const audioData = await fetchAudio(params);
const audioBlob = new Blob([audioData], { type: 'audio/wav' });
audioPlayer.src = URL.createObjectURL(audioBlob);
showStatus('语音合成成功!', 'success');
} catch (error) {
showStatus(`语音合成失败: ${error.message}`, 'error');
} finally {
synthesizeBtn.disabled = false;
}
});
async function fetchAudio(params) {
// 构造代理请求URL
const proxyUrl = 'http://localhost:3000/tts-proxy';
const query = new URLSearchParams({
...params,
text: params.text // 自动编码
}).toString();
const response = await fetch(`${proxyUrl}?${query}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`请求失败: ${response.status} ${errorText}`);
}
return await response.arrayBuffer();
}
function showStatus(message, type) {
/* 状态显示逻辑 */
}
});
</script>
</body>
</html>
前端直接调用阿里云语音合成API时,通常会遇到跨域请求限制(CORS)。浏览器出于安全考虑会阻止这类跨域请求。
解决方案
搭建Node.js代理服务器作为前端和阿里云API之间的中转层,解决跨域问题。以下是完整实现:
代理服务器实现(Node.js)
javascript
const express = require("express");
const axios = require("axios");
const app = express();
// 请求日志中间件
app.use((req, res, next) => {
console.log(`${new Date().toLocaleString()} - ${req.method} ${req.url}`);
next();
});
// 处理预检请求
app.options("/tts-proxy", (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With");
res.sendStatus(204);
});
// 语音合成代理接口
app.get("/tts-proxy", async (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
try {
const { appkey, token, text, voice, volume, speed } = req.query;
const apiUrl = `https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts?appkey=${appkey}&token=${token}&text=${encodeURIComponent(text)}&voice=${voice}&volume=${volume}&speech_rate=${speed}&format=wav`;
const response = await axios({
method: "post",
url: apiUrl,
responseType: "arraybuffer",
});
res.set("Content-Type", "audio/wav");
res.send(response.data);
} catch (error) {
console.error("代理错误:", error);
if (error.response) {
console.error("阿里云错误状态:", error.response.status);
console.error("阿里云错误数据:", error.response.data.toString());
}
res.status(500).json({
error: "语音合成失败",
details: error.message,
});
}
});
// 启动服务
const PORT = 3000;
app.listen(PORT, () => {
console.log(`代理服务运行在 http://localhost:${PORT}`);
});
实现原理
-
跨域处理:
- 代理服务器设置CORS响应头(
Access-Control-Allow-Origin: *
) - 处理OPTIONS预检请求
- 代理服务器设置CORS响应头(
-
请求转发:
- 前端调用本地代理接口(http://localhost:3000/tts-proxy)
- 代理服务器转发请求到阿里云API
- 代理服务器将音频数据返回给前端
-
错误处理:
- 捕获代理服务和阿里云API的错误
- 返回详细错误信息给前端
使用步骤
-
安装依赖:
javascriptnpm install express axios
-
启动代理服务:
javascriptnode server.js
-
访问前端页面:
javascripthttp://localhost:3000/index.html
-
输入参数并合成语音