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

Node.js Worker线程中等待音频下载完成后执行裁剪的方法

问题解决:Worker线程中音频下载完成后再执行裁剪

问题背景

现有Node.js应用通过Worker线程从API下载音频,希望在同一Worker线程内完成音频下载后立即执行裁剪操作,但当前裁剪代码会在下载完成前并行运行,导致生成空文件。

原代码

主进程代码

// 连接API
async function connectAPI() {
  await forLogin();
}

// 获取数据后创建Worker线程
async function startWorkers() {
  return new Promise((resolve, reject) => {
    // 存储数据的数组(原代码此处省略部分逻辑)
    threads.add(new Worker("worker.js", { workerData: { worker: id, data: dataperWorker[id] } }));

    for (let worker of threads) {
      worker.on("error", () => {
        // 错误处理(原代码此处省略逻辑)
      });

      worker.on("exit", () => {
        threads.delete(worker);
        return resolve; // 此处存在错误
      });
      worker.on("message", (message) => {
        // 接收Worker线程消息
        switch (message.type) {
          case "log":
            break;
        }
      });
    }
  });
}

Worker线程代码(worker.js)

async function downloadAudio(workerId, data, storagePath) {
  await forGetRecordings();

  const file = fs.createWriteStream(`${storagePath}/fileName.mp3`);
  https.get(recordingsURL, async (response) => {
    response.pipe(file);
  });
}

// 新增的音频裁剪函数
function splitAudio(data, storagePath) {
  return new Promise((resolve, reject) => {
    for (const value of data.values) {
      // 裁剪条件判断(原代码此处省略逻辑)
      ffmpeg(`${storagePath}/fileName.mp3`)
        .inputOptions([`-ss 40`, `-to 150`])
        .output(`${storagePath}/fileName_id.mp3`)
        .on("end", function () {
          logger.info(`Trimming records completed!`);
          resolve();
        })
        .on("error", function (err) {
          logger.error(`Error occured ${err.message}`);
          reject();
        })
        .run();
    }
  });
}

async function init(workerId, data) {
  await forLogin();
  // 创建本地文件夹用于下载音频(原代码此处省略逻辑)
  for (const i = 0; i < data.length; i++) {
    await downloadAudio(workerId, data[i], storagePath); // 下载完整音频
    await splitAudio(data[i], storagePath); // 执行裁剪
  }
}

init(workerData.worker, workerData.data);

核心问题分析

  • downloadAudio未等待下载完成:downloadAudio是async函数,但内部https.get和response.pipe(file)的异步操作没有被Promise包裹,导致await downloadAudio会直接跳过等待,立即执行后续的splitAudio,此时音频文件还未下载完成,裁剪时读取的是空文件。
  • splitAudio的Promise处理错误:如果data.values有多个元素,循环内会多次调用ffmpeg,但Promise只会在第一个ffmpeg完成时resolve或reject,后续的裁剪任务结果不会被捕获。
  • startWorkers中的resolve调用错误:原代码中return resolve;未实际调用resolve方法,导致Promise永远不会被决议。

修复方案

  • 1. 修复downloadAudio函数,等待下载完成

将https.get和文件流的操作包裹成Promise,确保下载完成后才resolve:

async function downloadAudio(workerId, data, storagePath) {
  await forGetRecordings();

  return new Promise((resolve, reject) => {
    const filePath = `${storagePath}/fileName.mp3`;
    const file = fs.createWriteStream(filePath);
    
    https.get(recordingsURL, (response) => {
      response.pipe(file);
      
      // 监听文件流完成事件
      file.on('finish', () => {
        file.close(); // 关闭文件流
        resolve();
      });
      
      // 监听文件流错误
      file.on('error', (err) => {
        fs.unlink(filePath, () => {}); // 删除不完整的文件
        reject(err);
      });
    }).on('error', (err) => {
      reject(err);
    });
  });
}
  • 2. 修复splitAudio函数,处理多个裁剪任务

用Promise.all等待所有ffmpeg任务完成后再resolve,同时确保输出文件名唯一:

async function splitAudio(data, storagePath) {
  const trimPromises = [];
  
  for (const value of data.values) {
    // 裁剪条件判断(原代码此处省略逻辑)
    const outputPath = `${storagePath}/fileName_${value.id}.mp3`; // 避免文件名冲突
    
    const trimPromise = new Promise((resolve, reject) => {
      ffmpeg(`${storagePath}/fileName.mp3`)
        .inputOptions([`-ss 40`, `-to 150`])
        .output(outputPath)
        .on("end", () => {
          logger.info(`Trimming completed for ${outputPath}`);
          resolve();
        })
        .on("error", (err) => {
          logger.error(`Error trimming ${outputPath}: ${err.message}`);
          reject(err);
        })
        .run();
    });
    
    trimPromises.push(trimPromise);
  }
  
  // 等待所有裁剪任务完成
  await Promise.all(trimPromises);
}
  • 3. 修复startWorkers中的resolve调用

将return resolve;改为resolve();,确保Promise能正确决议:

worker.on("exit", () => {
  threads.delete(worker);
  resolve(); // 正确调用resolve方法
});

修复后的执行流程

修改后,init函数中的await downloadAudio会等待音频完全下载到本地文件后,才会执行await splitAudio,确保裁剪时读取的是完整的音频文件;同时splitAudio会等待所有裁剪任务完成后才继续下一轮循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 06:50:54