You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何等待API完成本地音频文件写入后再上传至S3存储桶?

解决ElevenLabs TTS生成后立即上传S3失败的问题

问题描述

使用elevenlabs-api生成TTS音频并保存到本地后,立即上传到S3存储桶时代码执行失败。原因是文件写入磁盘需要时间(文件越大耗时越久),直接后续读取上传会遇到文件未写完的情况。尝试用setTimeout临时解决,但效率低、不灵活,且API请求仍会超时,不是理想方案。虽已在TextToSpeech和fileContent前加了await,但未起作用,对Node/Express的异步Promise机制理解不足。

解决方案

1. 确保TextToSpeech函数返回Promise

如果TextToSpeech是基于回调的函数,必须将其包装为Promise,保证文件完全写入后才执行后续逻辑:

// 包装回调式的TextToSpeech为Promise
function TextToSpeechPromise(apiKey, text, voice_id, fileurl) {
  return new Promise((resolve, reject) => {
    TextToSpeech(apiKey, text, voice_id, fileurl, (err, result) => {
      if (err) reject(err);
      else resolve(result);
    });
  });
}

调用时使用await TextToSpeechPromise(...),确保文件写完后再执行上传步骤。

2. 移除fs.createReadStream前的await

fs.createReadStream是同步创建流的方法,不返回Promise,await对它无效,直接创建即可:

const fileContent = fs.createReadStream('C:\\Users\\User\\Projects\\backend\\public\\upload\\text_to_speech\\' + fileName);

3. 使用Promise化的S3上传方法

原代码用回调式的putObject无法通过await等待上传完成,改用Promise形式确保上传完成后再结束请求:

await s3.putObject({
  Bucket: 'bucket',
  Key: `${fileName}`,
  Body: fileContent,
  ACL: 'public-read'
}).promise();

完整修正代码

const fs = require('fs');
const aws = require('aws-sdk');

// 假设TextToSpeech是回调式函数,先包装为Promise
function TextToSpeechPromise(apiKey, text, voice_id, fileurl) {
  return new Promise((resolve, reject) => {
    TextToSpeech(apiKey, text, voice_id, fileurl, (err, result) => {
      if (err) reject(err);
      else resolve(result);
    });
  });
}

exports.tts = async (req, res) => {
  try {
    // 等待音频文件完全写入本地
    let audio = await TextToSpeechPromise(apiKey, text, voice_id, fileurl);
    console.log(`Success, Audio saved as: ${fileName}`);

    const spacesEndpoint = new aws.Endpoint('endpoint');
    const s3 = new aws.S3({
      endpoint: spacesEndpoint,
      accessKeyId: process.env.AWSACCESSKEYID,
      secretAccessKey: process.env.SECRETACCESSKEY,
    });

    // 创建文件读取流
    const fileContent = fs.createReadStream('C:\\Users\\User\\Projects\\backend\\public\\upload\\text_to_speech\\' + fileName);

    // 等待S3上传完成
    await s3.putObject({
      Bucket: 'bucket',
      Key: `${fileName}`,
      Body: fileContent,
      ACL: 'public-read'
    }).promise();

    console.log('文件上传至S3成功');
    res.status(200).json({ message: '音频生成并上传成功', fileName });
  } catch (err) {
    console.error('操作失败:', err);
    res.status(500).json({ error: '音频生成或上传失败' });
  }
};

注意事项

  • 必须为async函数添加try/catch块,捕获异步操作中的错误,避免请求挂起或服务器崩溃。
  • 确认TextToSpeech函数的内部实现:如果它使用fs.writeFile等异步写入方法,必须等待写入完成才触发回调或resolve Promise,否则仍会出现文件未写完的问题。

内容的提问来源于stack exchange,提问作者Halnex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:32:52