为什么我在 NodeJS express 应用中上传不完整

问题描述

我需要在生成后将 v8 堆转储上传到 AWS S3 存储桶中,但是上传文件是 0KB 或 256KB。服务器上的文件大小超过 70MB,因此请求似乎不会等到堆转储没有完全刷新到磁盘。我猜想通过管道传输到 fs.createWriteStream 的可读流是以异步方式发生的,而调用函数await 实际上并不是在等待。我使用的是 v3 版本的 AWS NodeJS SDK。我做错了什么?

代码


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() 返回一个承诺,但该承诺与流完成时没有任何联系。因此,当调用者对该承诺使用 await 时,承诺在流实际完成之前很久就被解决了。 async 函数中没有任何魔法可以知道何时完成了像 .pipe() 这样的非承诺异步操作。因此,您的 async 函数返回一个与流函数完全没有联系的承诺。

由于流对 promise 没有太多的原生支持,您可以手动承诺流的完成和错误:

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))
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...