使用Node.js调用Google Cloud Speech-to-Text API遇错求助
问题背景
我是Google Cloud新手,创建了集成Speech-to-Text API的项目并关联账单账户,参考控制台内Node.js快速入门教程操作,修正教程代码后运行遇到错误。
第一个错误:RESOURCE_PROJECT_INVALID
运行代码后出现以下错误:
node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ Error: 3 INVALID_ARGUMENT: Invalid resource field value in the request. at callErrorFromStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/call.js:31:19) at Object.onReceiveStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client.js:192:76) at Object.onReceiveStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141) at Object.onReceiveStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78 at process.processTicksAndRejections (node:internal/process/task_queues:77:11) for call at at ServiceClientImpl.makeUnaryRequest (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client.js:160:32) at ServiceClientImpl.<anonymous> (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/@google-cloud/speech/build/src/v2/speech_client.js:318:29 at /home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/timeout.js:44:16 at repeat (/home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/retries.js:80:25) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/retries.js:119:13 at OngoingCallPromise.call (/home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/call.js:67:27) at NormalApiCaller.call (/home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/normalApiCaller.js:34:19) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/createApiCall.js:108:30 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { code: 3, details: 'Invalid resource field value in the request.', metadata: Metadata { internalRepr: Map(2) { 'google.rpc.errorinfo-bin' => [ Buffer(127) [Uint8Array] [ 10, 24, 82, 69, 83, 79, 85, 82, 67, 69, 95, 80, 82, 79, 74, 69, 67, 84, 95, 73, 78, 86, 65, 76, 73, 68, 18, 14, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 26, 32, 10, 7, 115, 101, 114, 118, 105, 99, 101, 18, 21, 115, 112, 101, 101, 99, 104, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 26, 49, 10, 6, 109, 101, 116, 104, 111, 100, 18, 39, 103, 111, 111, 103, 108, 101, 46, 99, 108, 111, 117, 100, ... 27 more items ] ], 'grpc-status-details-bin' => [ Buffer(222) [Uint8Array] [ 8, 3, 18, 44, 73, 110, 118, 97, 108, 105, 100, 32, 114, 101, 115, 111, 117, 114, 99, 101, 32, 102, 105, 101, 108, 100, 32, 118, 97, 108, 117, 101, 32, 105, 110, 32, 116, 104, 101, 32, 114, 101, 113, 117, 101, 115, 116, 46, 26, 171, 1, 10, 40, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 114, 112, 99, 46, 69, 114, 114, 111, 114, 73, 110, 102, 111, 18, 127, 10, 24, 82, 69, 83, ... 122 more items ] ] }, options: {} }, note: 'Exception occurred in retry method that was not classified as transient', statusDetails: [ ErrorInfo { metadata: { service: 'speech.googleapis.com', method: 'google.cloud.speech.v2.Speech.Recognize' }, reason: 'RESOURCE_PROJECT_INVALID', domain: 'googleapis.com' } ], reason: 'RESOURCE_PROJECT_INVALID', domain: 'googleapis.com', errorInfoMetadata: { service: 'speech.googleapis.com', method: 'google.cloud.speech.v2.Speech.Recognize' } }
已执行gcloud auth application-default set-quota-project $PROJECT_ID设置配额项目,Windows系统下执行相同步骤也出现相同错误。
初始代码:
const speech = require('@google-cloud/speech').v2; const https = require('https'); const client = new speech.SpeechClient(); const projectId = '<ProjectId>'; var recognizerName; const fileUrl = 'https://storage.googleapis.com/cloud-samples-data/speech/brooklyn_bridge.wav'; async function createRecognizer() { const recognizerRequest = { parent: `projects/${projectId}/locations/global`, recognizerId: 'en123', recognizer: { languageCodes: ['en-US'], model: 'latest_long', }, }; const operation = await client.createRecognizer(recognizerRequest); const recognizer = operation[0].result; recognizerName = recognizer.name; console.log(`Created new recognizer: ${recognizerName}`); } async function getUrlData(url) { return new Promise((resolve) => { https.get(url, response => { var body = ''; var i = 0; response.on('data', chunk => { i++; body += chunk; }); response.on('end', () => { resolve(Buffer.from(body).toString('base64')); }); }); }); } async function transcribeFile() { const content = await getUrlData(fileUrl); const transcriptionRequest = { recognizer: recognizerName, config: { autoDecodingConfig: {}, }, content: content, }; const response = await client.recognize(transcriptionRequest); // <=== 错误发生在此行 for (const result of response[0].results) { console.log(`Transcript: ${result.alternatives[0].transcript}`); } } createRecognizer(); transcribeFile();
尝试修改后出现第二个错误:音频编码不支持
修改recognizerName为完整路径projects/${projectId}/locations/global/recognizers/${recognizerId}后,代码如下:
const projectId = '<ProjectId>'; const recognizerId = 'en123'; const fileUrl = 'https://storage.googleapis.com/cloud-samples-data/speech/brooklyn_bridge.wav'; // ... 其他函数不变 async function transcribeFile() { const recognizerName = `projects/${projectId}/locations/global/recognizers/${recognizerId}`; const content = await getUrlData(fileUrl); const transcriptionRequest = { recognizer: recognizerName, config: { autoDecodingConfig: {}, }, content: content, }; const response = await client.recognize(transcriptionRequest); // <=== 错误发生在此行 for (const result of response[0].results) { console.log(`Transcript: ${result.alternatives[0].transcript}`); } } createRecognizer(); transcribeFile();
出现新错误:
Error: 3 INVALID_ARGUMENT: Audio data does not appear to be in a supported encoding. If you believe this to be incorrect, try explicitly specifying the decoding parameters. at callErrorFromStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/call.js:31:19) at Object.onReceiveStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client.js:192:76) at Object.onReceiveStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141) at Object.onReceiveStatus (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78 at process.processTicksAndRejections (node:internal/process/task_queues:77:11) for call at at ServiceClientImpl.makeUnaryRequest (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/client.js:160:32) at ServiceClientImpl.<anonymous> (/home/<Google-Account>/speech-to-text-nodejs/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/@google-cloud/speech/build/src/v2/speech_client.js:318:29 at /home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/timeout.js:44:16 at repeat (/home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/retries.js:80:25) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/retries.js:119:13 at OngoingCallPromise.call (/home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/call.js:67:27) at NormalApiCaller.call (/home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/normalCalls/normalApiCaller.js:34:19) at /home/<Google-Account>/speech-to-text-nodejs/node_modules/google-gax/build/src/createApiCall.js:108:30 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { code: 3, details: 'Audio data does not appear to be in a supported encoding. If you believe this to be incorrect, try explicitly specifying the decoding parameters.', metadata: Metadata { internalRepr: Map(0) {}, options: {} }, note: 'Exception occurred in retry method that was not classified as transient' }
问题原因及解决方法
第一个错误(RESOURCE_PROJECT_INVALID)的原因
初始代码中createRecognizer()和transcribeFile()是并行执行的,transcribeFile()尝试使用recognizerName时,createRecognizer()可能还未完成创建操作,导致recognizerName未被正确赋值,传递给API的资源字段无效。
解决方法:
确保createRecognizer()执行完成后再调用transcribeFile(),修改主函数调用逻辑:
// 替换原来的 createRecognizer(); transcribeFile(); async function run() { await createRecognizer(); await transcribeFile(); } run().catch(console.error);
第二个错误(音频编码不支持)的原因
getUrlData()函数中,将音频数据以字符串拼接的方式处理chunk,会破坏二进制音频数据的格式,导致转成base64后无法被API识别。
解决方法:
修改getUrlData()函数,用Buffer数组存储二进制数据,避免字符串拼接导致的格式损坏:
async function getUrlData(url) { return new Promise((resolve, reject) => { https.get(url, response => { const chunks = []; response.on('data', chunk => { chunks.push(chunk); }); response.on('end', () => { const buffer = Buffer.concat(chunks); resolve(buffer.toString('base64')); }); response.on('error', reject); }).on('error', reject); }); }
额外注意事项
- 确保项目已启用Speech-to-Text API,可在Google Cloud控制台的API库中确认。
- 若自动解码仍有问题,可在请求中显式指定音频编码参数,替换
autoDecodingConfig:
config: { explicitDecodingConfig: { encoding: 'LINEAR16', sampleRateHertz: 16000, languageCodes: ['en-US'] } }
(注:brooklyn_bridge.wav的编码为LINEAR16,采样率16000Hz)
内容的提问来源于stack exchange,提问作者cmd770

