从Firebase函数node.js服务器的tmp目录上传合成语音 [英] Upload synthesized speech from firebase function node.js server's tmp directory
问题描述
我正在尝试在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屋!