为什么我在 NodeJS Express 应用程序中上传不完整
Why is my upload incomplete in a NodeJS express app
我需要在生成 v8 堆转储后将其上传到 AWS S3 存储桶中,但上传的文件为 0KB 或 256KB。服务器上的文件大小超过 70MB,因此看起来请求不会等到堆转储没有完全刷新到磁盘。我猜想通过管道传输到 fs.createWriteStream
的可读流是以异步方式发生的,而调用函数的 await
实际上并没有在等待。我使用的是 AWS NodeJS SDK 的 v3 版本。我做错了什么?
代码
async function createHeapSnapshot (fileName) {
const snapshotStream = v8.getHeapSnapshot();
// It's important that the filename end with `.heapsnapshot`,
// otherwise Chrome DevTools won't open it.
const fileStream = fs.createWriteStream(fileName);
snapshotStream.pipe(fileStream);
}
async function pushHeapSnapshotToS3(fileName)
{
const heapDump = fs.createReadStream(fileName);
const s3Client = new S3Client();
const putCommand = new PutObjectCommand(
{
Bucket: "my-bucket",
Key: `heapdumps/${fileName}`,
Body: heapDump
}
)
return s3Client.send(putCommand);
}
app.get('/heapdump', asyncMiddleware(async (req, res) => {
const currentDateTime = Date.now();
const fileName = `${currentDateTime}.heapsnapshot`;
await createHeapSnapshot(fileName);
await pushHeapSnapshotToS3(fileName);
res.send({
heapdumpFileName: `${currentDateTime}.heapsnapshot`
});
}));
你猜对了。 createHeapSnapshot()
returns 是一个承诺,但该承诺与流何时完成完全没有关系。因此,当调用者对该承诺使用 await
时,承诺在流实际完成之前很久就已解决。 async
函数并没有什么魔力可以知道像 .pipe()
这样的非承诺异步操作何时完成。因此,您的 async
函数 returns 一个与流函数完全没有联系的承诺。
由于流对承诺的原生支持不多,您可以手动承诺流的完成和错误:
function createHeapSnapshot (fileName) {
return new Promise((resolve, reject) => {
const snapshotStream = v8.getHeapSnapshot();
// It's important that the filename end with `.heapsnapshot`,
// otherwise Chrome DevTools won't open it.
const fileStream = fs.createWriteStream(fileName);
fileStream.on('error', reject).on('finish', resolve);
snapshotStream.on('error', reject);
snapshotStream.pipe(fileStream);
});
}
或者,您可以使用更新的 pipeline()
function,它确实支持承诺(在 nodejs v15 中添加了内置承诺支持)并替换了 .pipe()
并具有内置错误监控以拒绝承诺:
const { pipeline } = require('stream/promises');
function createHeapSnapshot (fileName) {
const snapshotStream = v8.getHeapSnapshot();
// It's important that the filename end with `.heapsnapshot`,
// otherwise Chrome DevTools won't open it.
return pipeline(snapshotStream, fs.createWriteStream(fileName))
}
我需要在生成 v8 堆转储后将其上传到 AWS S3 存储桶中,但上传的文件为 0KB 或 256KB。服务器上的文件大小超过 70MB,因此看起来请求不会等到堆转储没有完全刷新到磁盘。我猜想通过管道传输到 fs.createWriteStream
的可读流是以异步方式发生的,而调用函数的 await
实际上并没有在等待。我使用的是 AWS NodeJS SDK 的 v3 版本。我做错了什么?
代码
async function createHeapSnapshot (fileName) {
const snapshotStream = v8.getHeapSnapshot();
// It's important that the filename end with `.heapsnapshot`,
// otherwise Chrome DevTools won't open it.
const fileStream = fs.createWriteStream(fileName);
snapshotStream.pipe(fileStream);
}
async function pushHeapSnapshotToS3(fileName)
{
const heapDump = fs.createReadStream(fileName);
const s3Client = new S3Client();
const putCommand = new PutObjectCommand(
{
Bucket: "my-bucket",
Key: `heapdumps/${fileName}`,
Body: heapDump
}
)
return s3Client.send(putCommand);
}
app.get('/heapdump', asyncMiddleware(async (req, res) => {
const currentDateTime = Date.now();
const fileName = `${currentDateTime}.heapsnapshot`;
await createHeapSnapshot(fileName);
await pushHeapSnapshotToS3(fileName);
res.send({
heapdumpFileName: `${currentDateTime}.heapsnapshot`
});
}));
你猜对了。 createHeapSnapshot()
returns 是一个承诺,但该承诺与流何时完成完全没有关系。因此,当调用者对该承诺使用 await
时,承诺在流实际完成之前很久就已解决。 async
函数并没有什么魔力可以知道像 .pipe()
这样的非承诺异步操作何时完成。因此,您的 async
函数 returns 一个与流函数完全没有联系的承诺。
由于流对承诺的原生支持不多,您可以手动承诺流的完成和错误:
function createHeapSnapshot (fileName) {
return new Promise((resolve, reject) => {
const snapshotStream = v8.getHeapSnapshot();
// It's important that the filename end with `.heapsnapshot`,
// otherwise Chrome DevTools won't open it.
const fileStream = fs.createWriteStream(fileName);
fileStream.on('error', reject).on('finish', resolve);
snapshotStream.on('error', reject);
snapshotStream.pipe(fileStream);
});
}
或者,您可以使用更新的 pipeline()
function,它确实支持承诺(在 nodejs v15 中添加了内置承诺支持)并替换了 .pipe()
并具有内置错误监控以拒绝承诺:
const { pipeline } = require('stream/promises');
function createHeapSnapshot (fileName) {
const snapshotStream = v8.getHeapSnapshot();
// It's important that the filename end with `.heapsnapshot`,
// otherwise Chrome DevTools won't open it.
return pipeline(snapshotStream, fs.createWriteStream(fileName))
}