从Firebase函数node.js服务器的tmp目录上传合成语音 [英] Upload synthesized speech from firebase function node.js server's tmp directory

查看:162
本文介绍了从Firebase函数node.js服务器的tmp目录上传合成语音的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在Firebase函数中上载Google的Text-to-Speech API返回的音频,并且无法将音频文件写入Node.js服务器的temp目录.我在函数日志中收到以下错误:

I am trying to upload the audio returned by Google's Text-to-Speech API in a Firebase Function and having trouble writing the audio file to the Node.js server's temp directory. I receive the following error in my functions log:

写入错误:{错误:ENOENT:没有此类文件或目录,请在错误(本机)错误号:-2,代码:"ENOENT",syscall:"open",路径:'/tmp/synthesized/output.mp3'}

Write ERROR: { Error: ENOENT: no such file or directory, open '/tmp/synthesized/output.mp3' at Error (native) errno: -2, code: 'ENOENT', syscall: 'open', path: '/tmp/synthesized/output.mp3' }

这是我的进口货

    // Cloud Storage
    import * as Storage from '@google-cloud/storage';
    const gcs = new Storage();

    import { tmpdir } from 'os';
    import { join, dirname } from 'path';
    import * as fs from 'fs';
    import * as fse from 'fs-extra';

    // Cloud Text to Speech
    import * as textToSpeech from '@google-cloud/text-to-speech';
    const client = new textToSpeech.TextToSpeechClient();

...以及我的部分功能遇到了麻烦

...and the part of my function I'm having trouble with:

    // Construct the text-to-speech request
    const request = {
        input: { text: text },
        voice: { languageCode: 'en-US', ssmlGender: 'NEUTRAL' },
        audioConfig: { audioEncoding: 'MP3' },
    };

    // Creat temp directory
    const workingDir = join(tmpdir(), 'synthesized');
    const tmpFilePath = join(workingDir, 'output.mp3');

    // Ensure temp directory exists
    await fse.ensureDir(workingDir);

    // Performs the Text-to-Speech request
    client.synthesizeSpeech(request)
        .then(responses => {
            const response = responses[0];
            // Write the binary audio content to a local file in temp directory
            fs.writeFile(tmpFilePath, response.audioContent, 'binary', writeErr => {
                if (writeErr) {
                    console.error('Write ERROR:', writeErr);
                    return;
                }
                // Upload audio to Firebase Storage
                gcs.bucket(fileBucket).upload(tmpFilePath, {
                    destination: join(bucketDir, pageName)
                })
                    .then(() => { console.log('audio uploaded successfully') })
                    .catch((error) => { console.log(error) });
            });
        })
        .catch(err => {
            console.error('Synthesize ERROR:', err);
        });

我的临时目录创建或fs.writeFile()函数出了什么问题?

What is wrong with my temp directory creation or fs.writeFile() function?

推荐答案

(为回答问题而修改了答案...)

(Answer edited in response to question edit...)

在您最初的问题中,您调用了

In your original question, you invoked

client.synthesizeSpeech(request, (err, response) => {...})

遵循Node的http回调模式,在该模式中,回调函数可以在响应完成之前启动.您随后的代码将调用假定响应内容的方法;如果响应仍然为空,则fs.writeFile()最初不写入任何内容,并且后续方法无法找到不存在的文件. (由于fs.writeFile()遵循相同的回调模式,您甚至可能在程序退出后发现output.mp3文件,因为fs将流输入.但是我敢打赌,您的Firebase方法不在等待.)

following Node's http callback pattern, in which the callback function may initiate before the response is complete. Your subsequent code calls methods that assume response content; if the response is still empty, fs.writeFile() writes nothing initially, and subsequent methods cannot find the non-existent file. (Because fs.writeFile() follows the same callback pattern, you might even discover that output.mp3 file after the program exits, because fs will stream the input. But I bet your Firebase methods aren't waiting.)

解决方案是使用Promises或async/await.查看 Google TextToSpeechClient类文档,看起来synthesizeSpeech方法支持此功能:

The solution is to use Promises or async/await. Looking at the Google TextToSpeechClient class docs, it looks like the synthesizeSpeech method supports this:

返回:Promise->数组.数组的第一个元素是一个代表SynthesizeSpeechResponse的对象.

Returns: Promise -> Array. The first element of the array is an object representing SynthesizeSpeechResponse.

示例:

client.synthesizeSpeech(request)
  .then(responses => {
      var response = responses[0];
      // doThingsWith(response)
  })
  .catch(err => {
      console.error(err);
  });

那应该可以解决client.synthesizeSpeech的问题,但是不幸的是fs.writeFile仍然是同步的.如果使用Node> 10,则可以使用本机fsPromise.writeFile方法;如果使用Node> 8,则可以使用util.promisify()fs.writeFile转换为promise.但是您已经在注释中指出您正在使用Node 6,因此我们将不得不手动执行操作.从此参考资料:

That should solve the problem with client.synthesizeSpeech, but unfortunately fs.writeFile is still synchronous. If you were using Node >10 you could use a native fsPromise.writeFile method, and if you were using Node >8 you could use util.promisify() to convert fs.writeFile to promises. But you've indicated in comments that you are using Node 6, so we'll have to do things manually. Thieving from this reference:

const writeFilePromise = (file, data, option) => {
    return new Promise((resolve, reject) => {
        fs.writeFile(file, data, option, error => {
            if (error) reject(error);
            resolve("File created! Time for the next step!");
        });
    });
};

client.synthesizeSpeech(request)
    .then(responses => {
        const response = responses[0];
        return writeFilePromise(tmpFilePath, response.audioContent, 'binary');
    })
    .then(() => {
        return gcs.bucket(fileBucket).upload(tmpFilePath, {
            destination: join(bucketDir, pageName)
        });
    })
    .then(() => {
        console.log('audio uploaded successfully');
        return null;
    })
    .catch((error) => { console.log(error) });

我已经使用.then构造编写了所有这些内容,但是自然地,如果您愿意这样做,也可以使用async/await.我希望这可以解决问题-它将迫使您的Firebase代码等待,直到fs.writeFile完成其工作.不幸的是,我也将所有错误检查都拖到了最后一个.catch块中.为了清楚起见,使事情变得有些冗长.我相信你可以做得更好.

I've written all of this using .then constructs, but naturally, you could also use async/await if you would rather do that. I hope this fixes things--it will force your Firebase code to wait until fs.writeFile has completed its job. I have also, unfortunately, smooshed all of the error checking into one final .catch block. And made things a bit verbose for clarity. I'm sure you can do better.

这篇关于从Firebase函数node.js服务器的tmp目录上传合成语音的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆