从 firebase 函数上传合成语音 node.js 服务器的 tmp 目录
Upload synthesized speech from firebase function node.js server's tmp directory
我正在尝试在 Firebase 函数中上传由 Google 的文本转语音 API 返回的音频,但无法将音频文件写入 Node.js 服务器的临时目录。我在函数日志中收到以下错误:
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();
...以及我遇到问题的函数部分:
// 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()
功能有什么问题?
(为响应问题编辑而编辑的答案...)
在你原来的问题中,你调用了
client.synthesizeSpeech(request, (err, response) => {...})
遵循 Node 的 http
回调模式,其中回调函数可能在响应完成之前启动。您的后续代码调用假定响应内容的方法;如果响应仍然为空,fs.writeFile()
最初什么也不写,后续方法无法找到不存在的文件。 (因为 fs.writeFile()
遵循相同的回调模式,您甚至可能会在程序退出后发现 output.mp3
文件,因为 fs
将流式传输输入。但我敢打赌您的 Firebase 方法不会等待.)
解决方案是使用 Promises 或 async/await。查看Google TextToSpeechClient
class docs,好像synthesizeSpeech
方法支持这个:
Returns: Promise -> Array. The first element of the array is an object representing SynthesizeSpeechResponse.
Example:
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
转换为 promises。但是您在评论中指出您正在使用 Node 6,因此我们必须手动执行操作。从 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
块中。为了清楚起见,让事情变得有点冗长。我相信你可以做得更好。
我正在尝试在 Firebase 函数中上传由 Google 的文本转语音 API 返回的音频,但无法将音频文件写入 Node.js 服务器的临时目录。我在函数日志中收到以下错误:
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();
...以及我遇到问题的函数部分:
// 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()
功能有什么问题?
(为响应问题编辑而编辑的答案...)
在你原来的问题中,你调用了
client.synthesizeSpeech(request, (err, response) => {...})
遵循 Node 的 http
回调模式,其中回调函数可能在响应完成之前启动。您的后续代码调用假定响应内容的方法;如果响应仍然为空,fs.writeFile()
最初什么也不写,后续方法无法找到不存在的文件。 (因为 fs.writeFile()
遵循相同的回调模式,您甚至可能会在程序退出后发现 output.mp3
文件,因为 fs
将流式传输输入。但我敢打赌您的 Firebase 方法不会等待.)
解决方案是使用 Promises 或 async/await。查看Google TextToSpeechClient
class docs,好像synthesizeSpeech
方法支持这个:
Returns: Promise -> Array. The first element of the array is an object representing SynthesizeSpeechResponse.
Example:
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
转换为 promises。但是您在评论中指出您正在使用 Node 6,因此我们必须手动执行操作。从 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
块中。为了清楚起见,让事情变得有点冗长。我相信你可以做得更好。