为什么 Busboy 在解析 FLAC 文件时会产生不一致的结果?
Why does Busboy yield inconsistent results when parsing FLAC files?
我有一个 Express 服务器接收 FormData
和附加的 FLAC 音频文件。对于大小不同 (10 - 70MB) 的几个文件,代码按预期工作,但其中一些文件卡在 'file'
事件中,我无法弄清楚为什么会发生这种情况。更奇怪的是,以前没有触发 file.on('close', => {})
事件的文件(如 Busboy 的文档中所示)突然触发,文件已成功上传。
对我来说,这似乎完全是随机的,因为我已经对十几个不同大小和内容类型的文件进行了尝试 (audio/flac & audio/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 - Buxton 的沉睡草地
来源:https://fenomenon.bandcamp.com/track/sleepy-meadows-of-buxton
- 大小:30.3MB
- 编解码器:FLAC
- MIME:audio/flac
三次尝试的结果:
- 加载了 18.74MB,然后卡住了
- 加载了 5.05MB,然后卡住了
- 加载了 21.23MB,然后卡住了
第二个测试用例:Almunia - New Moon
来源:https://almunia.bandcamp.com/track/new-moon
- 大小:38.7MB
- 编解码器:FLAC
- MIME:audio/flac
三次尝试的结果:
- 加载了 12.78MB,然后卡住了
- 加载38.65,上传成功!
- 加载38.65,上传成功!
如您所见,至少可以说这种行为是不可预测的。此外,这两个成功的上传确实从 Firebase 存储无缝回放,因此它确实按预期工作。我不明白的是为什么它不能总是工作,或者至少在大多数时候不能工作,排除任何与网络相关的故障。
更新:2
我无可救药地试图弄清楚这个问题,所以我现在创建了一个与我的实际项目非常相似的场景,并将代码上传到 GitHub。它非常小,但我确实添加了一些额外的库以使前端使用起来愉快。
除了用于后端的 Express 服务器和用于前端的简单 Vue 应用程序之外,它没有太多内容。在 files 文件夹中,有两个 FLAC 文件;其中一个只有 4.42MB,证明代码有时确实有效。另一个文件要大得多,有 38.1MB,可以可靠地说明问题。随意尝试任何其他文件。
请注意,必须修改前端以允许 FLAC 文件以外的文件。我选择只接受 FLAC 文件,因为这是我在实际项目中使用的文件。
当 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);
});
我有一个 Express 服务器接收 FormData
和附加的 FLAC 音频文件。对于大小不同 (10 - 70MB) 的几个文件,代码按预期工作,但其中一些文件卡在 'file'
事件中,我无法弄清楚为什么会发生这种情况。更奇怪的是,以前没有触发 file.on('close', => {})
事件的文件(如 Busboy 的文档中所示)突然触发,文件已成功上传。
对我来说,这似乎完全是随机的,因为我已经对十几个不同大小和内容类型的文件进行了尝试 (audio/flac & audio/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 - Buxton 的沉睡草地
来源:https://fenomenon.bandcamp.com/track/sleepy-meadows-of-buxton
- 大小:30.3MB
- 编解码器:FLAC
- MIME:audio/flac
三次尝试的结果:
- 加载了 18.74MB,然后卡住了
- 加载了 5.05MB,然后卡住了
- 加载了 21.23MB,然后卡住了
第二个测试用例:Almunia - New Moon
来源:https://almunia.bandcamp.com/track/new-moon
- 大小:38.7MB
- 编解码器:FLAC
- MIME:audio/flac
三次尝试的结果:
- 加载了 12.78MB,然后卡住了
- 加载38.65,上传成功!
- 加载38.65,上传成功!
如您所见,至少可以说这种行为是不可预测的。此外,这两个成功的上传确实从 Firebase 存储无缝回放,因此它确实按预期工作。我不明白的是为什么它不能总是工作,或者至少在大多数时候不能工作,排除任何与网络相关的故障。
更新:2
我无可救药地试图弄清楚这个问题,所以我现在创建了一个与我的实际项目非常相似的场景,并将代码上传到 GitHub。它非常小,但我确实添加了一些额外的库以使前端使用起来愉快。
除了用于后端的 Express 服务器和用于前端的简单 Vue 应用程序之外,它没有太多内容。在 files 文件夹中,有两个 FLAC 文件;其中一个只有 4.42MB,证明代码有时确实有效。另一个文件要大得多,有 38.1MB,可以可靠地说明问题。随意尝试任何其他文件。
请注意,必须修改前端以允许 FLAC 文件以外的文件。我选择只接受 FLAC 文件,因为这是我在实际项目中使用的文件。
当 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);
});