Загрузить синтезированную речь из директории tmp сервера firebase function node.js - PullRequest
0 голосов
/ 25 сентября 2018

Я пытаюсь загрузить звук, возвращенный Google Text-to-Speech API, в функцию Firebase, и у меня возникают проблемы с записью аудиофайла во временный каталог сервера Node.js.Я получаю следующую ошибку в моем журнале функций:

Ошибка записи: {Ошибка: ENOENT: нет такого файла или каталога, откройте /tmp/synthesized/output.mp3 'в Error (native) errno: -2, код: 'ENOENT', системный вызов: 'open', путь: '/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() функцией?

1 Ответ

0 голосов
/ 26 сентября 2018

(Ответ отредактирован в ответ на редактирование вопроса ...)

В исходном вопросе вы вызвали

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

по шаблону обратного вызова http узла, в котором используется функция обратного вызоваможет инициировать до завершения ответа.Ваш последующий код вызывает методы, которые предполагают содержание ответа;если ответ по-прежнему пуст, fs.writeFile() изначально ничего не записывает, а последующие методы не могут найти несуществующий файл.(Поскольку fs.writeFile() следует той же схеме обратного вызова, вы можете даже обнаружить, что файл output.mp3 после выхода из программы, потому что fs будет передавать поток данных. Но я уверен, что ваши методы Firebase не ждут.)

Решение - использовать Promises или async / await.Глядя на Google TextToSpeechClient класс документов , похоже, что метод synthesizeSpeech поддерживает это:

Возвращает: Promise -> Array.Первым элементом массива является объект, представляющий 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 в обещания.Но вы указали в комментариях, что используете Node 6, поэтому нам придется делать все вручную.Отбросив от эту ссылку :

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 блок.И сделал вещи немного многословными для ясности.Я уверен, что вы можете сделать лучше.

...