如何在Angular中安全实现Google Speech-To-Text流式识别?
在Angular客户端安全实现Google Speech-To-Text流式处理的方案
问题背景
原本在Node.js Firebase Functions中通过@google-cloud/speech实现非流式语音转文字,但无法满足流式处理需求,计划迁移到Angular客户端实现,需要安全的认证方案,且希望避免引入gRPC相关库带来的额外工作量。
原Firebase Functions代码:
import {SpeechClient} from "@google-cloud/speech"; export const transcriptAudio = onCall(async (data) => { let userId = data.auth?.token?.uid; if(!userId) throw new HttpsError('unauthenticated', 'The user is not authenticated.'); let audioByte = data.data.audioByte as string; // Detects speech in the audio file const client = new SpeechClient(); const response = await client.recognize( { audio: {content: audioByte}, config: { encoding: "LINEAR16", audioChannelCount: 2, languageCode: "en-US"}}); const transcription = response[0].results?.map(result => result.alternatives![0].transcript).join('\n'); return transcription; });
一、安全认证方案(基于Firebase Functions生成受限令牌)
1. 在Firebase Functions中添加令牌生成接口
通过Callable Function验证用户身份后,生成仅授权访问Speech-To-Text流式API的短期访问令牌,避免直接暴露服务账号密钥:
import { onCall, HttpsError } from "firebase-functions/v2/https"; import { GoogleAuth } from "google-auth-library"; export const getSpeechToken = onCall(async (request) => { const userId = request.auth?.token?.uid; if (!userId) { throw new HttpsError('unauthenticated', '用户未认证'); } const auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'] }); const client = await auth.getClient(); // 生成有效期1小时的令牌,可根据需求调整 const tokenResponse = await client.getAccessToken(); return { accessToken: tokenResponse.token, expiresAt: tokenResponse.res!.data.expires_in }; });
注意:需确保Firebase Functions的服务账号拥有
roles/speech.client权限,避免过度授权。
2. Angular客户端获取令牌并发起请求
在Angular组件中调用上述Callable Function获取令牌,之后用该令牌请求Speech-To-Text API:
import { AngularFireFunctions } from '@angular/fire/compat/functions'; import { HttpClient } from '@angular/common/http'; export class SpeechComponent { private speechToken: string | null = null; constructor( private fireFunctions: AngularFireFunctions, private http: HttpClient ) {} async getAuthToken(): Promise<void> { const getToken = this.fireFunctions.httpsCallable('getSpeechToken'); const result = await getToken({}).toPromise(); this.speechToken = result.accessToken; } }
二、简化流式处理的替代方案(无需gRPC)
Google Speech-To-Text支持REST流式API(基于HTTP/2),无需引入gRPC或ngx-grpc库,可直接用Angular的HttpClient结合RxJS处理流式响应:
1. 流式请求核心代码
startStreamingRecognition(audioStream: ReadableStream) { if (!this.speechToken) { throw new Error('未获取到认证令牌'); } const url = 'https://speech.googleapis.com/v1/speech:streamingRecognize'; // 构建请求体,先发送配置信息,再流式发送音频数据 const requestBody = new ReadableStream({ async start(controller) { // 发送初始配置帧 controller.enqueue(JSON.stringify({ streamingConfig: { config: { encoding: 'LINEAR16', audioChannelCount: 2, languageCode: 'en-US' }, interimResults: true // 返回实时中间识别结果 } }) + '\r\n'); // 流式转发音频数据 const reader = audioStream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; // 将音频二进制数据包装成JSON帧发送 controller.enqueue(JSON.stringify({ audioContent: btoa(String.fromCharCode(...new Uint8Array(value))) }) + '\r\n'); } controller.close(); } }); // 发起流式POST请求 this.http.post(url, requestBody, { headers: { 'Authorization': `Bearer ${this.speechToken}`, 'Content-Type': 'application/json' }, responseType: 'text', observe: 'response' }).subscribe({ next: (response) => { // 解析流式响应的每一行结果 const chunks = response.body?.split('\r\n').filter(chunk => chunk); chunks?.forEach(chunk => { const result = JSON.parse(chunk); const transcript = result.results?.[0]?.alternatives?.[0]?.transcript; if (transcript) { console.log('识别结果:', transcript); // 更新UI展示识别内容 } }); }, error: (err) => { console.error('流式识别出错:', err); } }); }
2. 关键说明
- 该方案基于HTTP/2的分块传输,Angular的HttpClient会自动处理流式响应
interimResults: true配置可返回实时的中间识别结果,满足流式场景需求- 音频数据需按要求编码为Base64字符串后发送
内容的提问来源于stack exchange,提问作者mctuna
相关产品推荐
相关产品推荐

