使用Google Cloud Storage将来自Google Text-to-Speech的音频文件保存到Firebase Storage?

5
我们正在尝试使用Google Cloud Function从Google Text-to-Speech获取音频文件并将其保存到Firebase Storage。Google Text-to-Speech的文档展示了如何获取音频文件并将其保存在本地:
// Performs the Text-to-Speech request
const [response] = await client.synthesizeSpeech(request);
// Write the binary audio content to a local file
const writeFile = util.promisify(fs.writeFile);
await writeFile('output.mp3', response.audioContent, 'binary');
console.log('Audio content written to file: output.mp3');

这会导致一个错误信息Error: EROFS: read-only file system。Google Cloud Storage不允许在本地写入文件。
使用Firebase Storage的bucket.upload()存在一些问题:
   const destinationPath = 'Audio/Spanish' + filename.ogg;
   // Performs the Text-to-Speech request
   const [response] = await client.synthesizeSpeech(request);
   // response.audioContent is the downloaded file
   await bucket.upload(response.audioContent, {
      destination: destinationPath
   ));

错误信息为 TypeError: Path must be a stringbucket.upload() 的第一个参数是要上传到您的存储桶中的文件的完全限定路径,并且应该是字符串,所以 response.audioContent 不起作用。

bucket.upload()文档建议我们将路径放在 destination: destinationPath 中作为 Firebase 存储位置。这正确吗?

我们如何将来自 Google 文字转语音(response.audioContent)的音频文件保存为字符串,以便 bucket.upload() 可以访问?或者我们应该使用其他东西来替代 bucket.upload() 吗?

以下是我们的完整云函数:

exports.Google_T2S = functions.firestore.document('Users/{userID}/Spanish/T2S_Request').onUpdate((change, context) => {
  if (change.after.data().word != undefined) {

    // Performs the Text-to-Speech request
    async function test() {
      try {
        const word = change.after.data().word; // the text
        const longLanguage = 'Spanish';
        const audioFormat = '.mp3';
        // copied from https://cloud.google.com/text-to-speech/docs/quickstart-client-libraries#client-libraries-usage-nodejs
        const fs = require('fs');
        const util = require('util');
        const textToSpeech = require('@google-cloud/text-to-speech'); // Imports the Google Cloud client library
        const client = new textToSpeech.TextToSpeechClient(); // Creates a client

        let myWordFile = word.replace(/ /g,"_"); // replace spaces with underscores in the file name
        myWordFile = myWordFile.toLowerCase(); // convert the file name to lower case
        myWordFile = myWordFile + audioFormat; // append .mp3 to the file name;

        // copied from https://cloud.google.com/blog/products/gcp/use-google-cloud-client-libraries-to-store-files-save-entities-and-log-data
        const {Storage} = require('@google-cloud/storage');
        const storage = new Storage();
        const bucket = storage.bucket('myProject-cd99d.appspot.com');
        const destinationPath = 'Audio/Spanish/' + myWordFile;

        const request = { // Construct the request
          input: {text: word},
          // Select the language and SSML Voice Gender (optional)
          voice: {languageCode: 'es-ES', ssmlGender: 'FEMALE'},
          // Select the type of audio encoding
          audioConfig: {audioEncoding: 'MP3'},
        };

        const [response] = await client.synthesizeSpeech(request);
        // Write the binary audio content to a local file
        const writeFile = util.promisify(fs.writeFile);
        await writeFile('output.mp3', response.audioContent, 'binary');
        console.log('Audio content written to file: output.mp3')
        // response.audioContent is the downloaded file

        await bucket.upload(response.audioContent, {
          destination: destinationPath
        });
      }
      catch (error) {
        console.error(error);
      }
    }
    test();
  } // close if
  return 0; // prevents an error message "Function returned undefined, expected Promise or value"
});
1个回答

7

file.save()是答案。 util.promisify是不必要的,并且会导致关于original的错误消息。 这是完成的云函数:

const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//  response.send("Hello from Firebase!");
// });

async function textToSpeechRequest() {
    try 
              {
                const word = change.after.data().word; // the text
                const longLanguage = 'Spanish';
                const audioFormat = '.mp3';
                // copied from https://cloud.google.com/text-to-speech/docs/quickstart-client-libraries#client-libraries-usage-nodejs
                const util = require('util');
                const textToSpeech = require('@google-cloud/text-to-speech'); // Imports the Google Cloud client library
                const client = new textToSpeech.TextToSpeechClient(); // Creates a client

                let myWordFile = word.replace(/ /g,"_"); // replace spaces with underscores in the file name
                myWordFile = myWordFile.toLowerCase(); // convert the file name to lower case
                myWordFile = myWordFile + audioFormat; // append .mp3 to the file name;

                // copied from https://cloud.google.com/blog/products/gcp/use-google-cloud-client-libraries-to-store-files-save-entities-and-log-data
                const { Storage } = require('@google-cloud/storage');
                const storage = new Storage();
                const bucket = storage.bucket('myProject-cd99d.appspot.com');
                var file = bucket.file('Audio/Spanish/' + myWordFile);

                const request = { // Construct the request
                  input: {text: word},
                  // Select the language and SSML Voice Gender (optional)
                  voice: {languageCode: 'es-ES', ssmlGender: 'FEMALE'},
                  // Select the type of audio encoding
                  audioConfig: {audioEncoding: 'MP3'},
                };

                const options = { // construct the file to write
                  metadata: {
                    contentType: 'audio/mpeg',
                    metadata: {
                      source: 'Google Text-to-Speech'
                    }
                  }
                };

                // copied from https://cloud.google.com/text-to-speech/docs/quickstart-client-libraries#client-libraries-usage-nodejs
                const [response] = await client.synthesizeSpeech(request);
                // Write the binary audio content to a local file
                // response.audioContent is the downloaded file
                return await file.save(response.audioContent, options)
                .then(() => {
                  console.log("File written to Firebase Storage.")
                  return;
                })
                .catch((error) => {
                  console.error(error);
                });
            } // close try
            catch (error) {
              console.error(error);
            } // close catch
    } // close async function declaration
  
    exports.Google_T2S = functions.firestore.document('Users/{userID}/Spanish/T2S_Request').onUpdate((change, context) => {
          if (change.after.data().word !== undefined) 
          {
            textToSpeechRequest();
          } // close if
      
      
    }); // close Google_T2S

我们遇到了一个错误 TypeError: [ERR_INVALID_ARG_TYPE]: The "original" argument must be of type function at Object.promisify。这个错误似乎不会影响云函数的运行。
再次强调一下没有成功的尝试,fs.createWriteStream 没有成功,因为 Google Cloud Functions 无法处理 Node 文件系统 命令。相反,Google Cloud Functions 有自己的 方法 来包装 Node 文件系统命令。bucket.upload() 可以将本地文件上传到存储桶,但本地文件的路径必须是字符串,不能是来自 API 的缓冲区或流。file.save() 被文档化为:

向文件中写入任意数据。

这是一个方便的方法,它包装了 File#createWriteStream

那就是我想要的!关于我的数据,有一件事是任意的。或者说本质上是相反的。之后我们只需要整理一下contentType(不是mp3,而是audio/mpeg)和文件路径。

1
为什么 const bucket.. 被注释掉了? - arielhasidim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接