【问题标题】:Why does Busboy yield inconsistent results when parsing FLAC files?为什么 Busboy 在解析 FLAC 文件时会产生不一致的结果?
【发布时间】:2022-01-25 22:04:11
【问题描述】:

我有一个 Express 服务器,它接收带有附加 FLAC 音频文件的 FormData。该代码对几个不同大小(10 - 70MB)的文件按预期工作,但其中一些文件卡在'file' 事件中,我无法弄清楚为什么会发生这种情况。更奇怪的是,以前没有触发file.on('close', => {}) 事件的文件(如Busboy 的文档中所示)突然触发,并且文件已成功上传。

对我来说,这似乎完全是随机的,因为我已经尝试了十几个不同大小和内容类型(音频/flac 和音频/x-flac)的文件,但结果不一致。然而,有些文件根本不起作用,即使我尝试多次解析它们。然而,如果有足够的尝试,某些文件可以被解析和上传?

'file' 事件中是否有一些我未能处理的错误?我确实尝试收听file.on('error', => {}) 事件,但没有发现任何错误。 Other answers 建议必须使用 file 流以使 'close' 事件继续进行,但我认为 file.pipe(fs.createWriteStream(fileObject.filePath)); 会这样做,对吗?

如果我忘记在我的问题中包含一些重要信息,请告诉我。这已经困扰我大约一周了,所以我很乐意提供任何相关的信息来帮助我克服这个障碍。

app.post('/upload', (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  const bb = busboy({ headers: request.headers });

  const fields = {};
  const fileObject = {};

  bb.on('file', (_name, file, info) => {
    const { filename, mimeType } = info;

    fileObject['mimeType'] = mimeType;
    fileObject['filePath'] = path.join(os.tmpdir(), filename);
    file.pipe(fs.createWriteStream(fileObject.filePath));

    file.on('close', () => {
      console.log('Finished parsing of file');
    });
  });

  bb.on('field', (name, value) => {
    fields[name] = value;
  });

  bb.on('close', () => {
    bucket.upload(
      fileObject.filePath,
      {
        uploadType: 'resumable',
        metadata: {
          metadata: {
            contentType: fileObject.mimeType,
            firebaseStorageDownloadToken: fields.id
          }
        }
      },
      (error, uploadedFile) => {
        if (error) {
          console.log(error);
        } else {
          db.collection('tracks')
            .doc(fields.id)
            .set({
              identifier: fields.id,
              artist: fields.artist,
              title: fields.title,
              imageUrl: fields.imageUrl,
              fileUrl: `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o/${uploadedFile.name}?alt=media&token=${fields.id}`
            });

          response.send(`File uploaded: ${fields.id}`);
        }
      }
    );
  });

  request.pipe(bb);
});

更新:1

我决定用file.on('data', (data) => {}) 测量每次上传时传输的字节数,只是为了看看问题是否总是相同的,结果证明这也是完全随机的。

let bytes = 0;

file.on('data', (data) => {
  bytes += data.length;

  console.log(`Loaded ${(bytes / 1000000).toFixed(2)}MB`);
});

第一个测试用例:Fenomenon - 巴克斯顿昏昏欲睡的草地

来源:https://fenomenon.bandcamp.com/track/sleepy-meadows-of-buxton

  • 大小:30.3MB
  • 编解码器:FLAC
  • MIME:音频/flac

三次尝试的结果:

  1. 加载了 18.74MB,然后卡住了
  2. 加载了 5.05MB,然后卡住了
  3. 加载了 21.23MB,然后卡住了

第二个测试用例:Almunia - New Moon

来源:https://almunia.bandcamp.com/track/new-moon

  • 大小:38.7MB
  • 编解码器:FLAC
  • MIME:音频/flac

三次尝试的结果:

  1. 加载了 12.78MB,然后卡住了
  2. 已加载38.65,已成功上传!
  3. 已加载38.65,已成功上传!

正如您所见,这种行为至少可以说是不可预测的。此外,这两个成功的上传确实从 Firebase 存储无缝播放,所以它确实按预期工作。我无法理解的是为什么它并不总是有效,或者至少在大多数情况下,排除任何与网络相关的故障。

更新:2

我绝望地试图理解这个问题,所以我现在创建了一个与我的实际项目非常相似的场景,并将代码上传到GitHub。它非常小,但我确实添加了一些额外的库以使前端使用起来愉快。

除了一个用于后端的 Express 服务器和一个用于前端的简单 Vue 应用程序之外,它并没有太多的东西。在 files 文件夹中,有两个 FLAC 文件;其中一个只有 4.42MB,以证明代码有时确实有效。另一个文件要大得多,为 38.1MB,以可靠地说明问题。随意尝试任何其他文件。

请注意,必须修改前端以允许 FLAC 文件以外的文件。我选择只接受 FLAC 文件,因为这是我在实际项目中使用的。

【问题讨论】:

  • 我在启用这些选项的情况下运行了一些测试,但不幸的是这并没有什么不同:file.pipe(fs.createWriteStream(fileObject.filePath, { autoClose: true, emitClose: true }));createWriteStream() 不会自动触发 'close' 事件吗?
  • 我明白你在说什么,但流有时会自动关闭,所以它应该可以工作。我已经用一些示例更新了我的原始问题,这些示例说明了我的实现是多么不可预测。
  • 相关? github.com/mscdex/busboy/issues/264 - tl:dr 正在监听从 createWriteStream() 返回的 writeStream 上的 finish 事件之前你调用 bucket.upload()。为什么?在您尝试使用结果之前,并非所有数据都已从缓冲区中刷新。
  • 这是有道理的。我现在等待createWriteStream() 发出'finish' 事件。问题似乎是不保证会发生此事件,这可能是问题的根源。有时,没有事件,也没有任何错误。我的代码:file.pipe(fs.createWriteStream(fileObject.filePath)).on('finish', () => {});.
  • 所以接下来的想法是.pipe() readableend() 的信号,因此在可写完成之前关闭writable?您可以通过添加{end: false} 作为调用.pipe() 的第二个参数来测试这一点。请注意,当readable中出现错误情况时,不会关闭可写对象的情况@

标签: javascript express busboy


【解决方案1】:

当 BusBoy 发出文件事件时,您需要直接写入文件。

如果您依赖 BusBoy 会阻止文件加载完成,似乎存在竞争条件。如果你在 file 事件处理程序中加载它,那么它工作正常。

app.post('/upload', (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  const bb = busboy({
    headers: request.headers
  });
  const fileObject = {};
  let bytes = 0;
  bb.on('file', (name, file, info) => {
    const {
      filename,
      mimeType
    } = info;
    fileObject['mimeType'] = mimeType;
    fileObject['filePath'] = path.join(os.tmpdir(), filename);
    const saveTo = path.join(os.tmpdir(), filename);
    const writeStream = fs.createWriteStream(saveTo);
    file.on('data', (data) => {
      writeStream.write(data);
      console.log(`Received: ${((bytes += data.length) / 1000000).toFixed(2)}MB`);
    });
    file.on('end', () => {
      console.log('closing writeStream');
      writeStream.close()
    });
  });
  bb.on('close', () => {
    console.log(`Actual size is ${(fs.statSync(fileObject.filePath).size / 1000000).toFixed(2)}MB`);
    console.log('This is where the file would be uploaded to some cloud storage server...');
    response.send('File was uploaded');
  });
  bb.on('error', (error) => {
    console.log(error);
  });
  request.pipe(bb);
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    相关资源
    最近更新 更多