我使用GoT.stream下载一个文件,并以流的方式将其发送到客户端.

我想要有一个监视器,有多少下载在任何给定的时刻,为此,我需要知道下载结束时,以便更新计数器.

const stream = require('stream');
const { promisify } = require('util');
const pipeline = promisify(stream.pipeline);
const got = require('got');


let currentDownloadsStream = 0;

app.get('download', async (req, res) => {
    currentDownloadsStream++;
    const downloader = got.stream(url);
    return pipeline(downloader, res)
        .finally(() => currentDownloadsStream--)
});

问题是,如果客户端在中间取消了下载,则不会调用Finally方法,似乎promise 会永远留在内存中,永远不会得到解决.

我试着加了

res.on('close', () => {
    downloader.destroy();
})

但这也没有帮助,如果我在浏览器中取消下载,则此事件不会触发.

如何判断客户端是否在下载完成之前停止了下载?

NodeJS v16.17.0


编辑:感谢@jFriend00帮我发现问题,问题是webpack开发服务器导致的,在生产中事件正常发出.

推荐答案

问题是,如果客户端在中间取消了下载,则不会调用Finally方法,似乎promise 会永远留在内存中,永远不会得到解决.

如果浏览器下载真的被取消,我无法在Chrome或Edge和NodeJS版本16.14.2中重现这个问题.当我开始一个巨大的下载,并通过点击Chrome下载栏中的"取消"选项来取消下载时,我立即得到了一系列事件,并看到管道promise 拒绝并拨打.finally().

我想知道你是不是真的要在浏览器中取消下载.在Chrome和Edge(我没有try 过其他浏览器)中,仅仅关闭下载的标签或窗口并不会停止下载.您必须物理取消下载,或者退出整个浏览器,并在出现停止下载的提示时回答"是".

以下是我在Chrome和Edge中手动取消下载时收到的事件:

  • 关闭res流上的事件
  • 下载程序上的错误事件:ERR_STREAM_PROFILE_CLOSE
  • 下载程序流上的关闭事件
  • .catch()%的管道promise :ERR_STREAM_PROFILE_CLOSE
  • .finally()%的流水线promise
  • 下载程序流上的错误事件:ECONNRESET

在Edge浏览器中,如果我完全退出浏览器,并在提示取消"正在进行"下载时回答"是",那么我会得到:

  • 关闭res流上的事件
  • 下载程序上的错误事件:ERR_STREAM_PROFILE_CLOSE
  • 下载程序流上的关闭事件
  • .catch()%的流水线promise
  • .finally()%的流水线promise
  • 下载程序流上的错误事件:ECONNRESET

仅供参考,我使用这个URL进行大型下载:"http://speedtest-sgp1.digitalocean.com/5gb.test"From This Site:https://testfiledownload.com/作为大型下载的来源.

以下是我的测试代码:

const express = require('express');
const stream = require('stream');
const { pipeline } = require('node:stream/promises');
const got = require('got');

const app = express();

const url = "http://speedtest-sgp1.digitalocean.com/5gb.test";


let currentDownloadsStream = 0;

app.get('/download', async (req, res) => {
    currentDownloadsStream++;
    const downloader = got.stream(url);

    downloader.on('end', () => {
        console.log("end event on downloader");
    }).on('close', () => {
        console.log("close event on downloader");
    }).on('error', err => {
        console.log("error event on downloader");
        console.log(err);
    });

    res.on('finish', () => {
        console.log("finish event");
    }).on('end', () => {
        console.log("end event on res");
    }).on('prefinish', () => {
        console.log("prefinish eventon res");
    }).on('close', () => {
        console.log("close event on res");
    }).on('error', err => {
        console.log("error event on res");
        console.log(err);
    });

    pipeline(downloader, res).then(result => {
        console.log(".then() called");
    }).catch(err => {
        console.log(".catch() called");
        console.log(err);
    }).finally(() => {
        console.log(".finally() called");
        currentDownloadsStream--;
    })
});

app.listen(80);

Node.js相关问答推荐

如何呈现ejs.renderFile中包含的子模板?

如果我加入另一个公会且我的​​机器人已在其中,欢迎消息发送错误

在 NodeJS 中使用 post 时出现错误 500TypeError: 无法解构 'req.body' 的属性 'name',因为它未定义

PM2 是否需要成为其托管项目的依赖项?

动态设置元数据,无需重复请求 NextJS 13

在构建完成后,将AddedFiles文件夹的内容复制到dist文件夹

2023年如何在Node.js中使用Gmail发送邮箱?

我如何在 Raku 的供应中注册不同的事件?

错误:0308010C:try 通过 Github 推送部署到 firebase 托管时出现数字

使用 Forms API 进行批量更新时生成 itemId

自定义 Docker 容器 Github 操作无法在 /github/workspace 中找到 Node 脚本

如何申请在NextJS上下载文件的许可?

如何正确使用 package.json 中的关键字属性?

等价于 Node.js 的 Rails 控制台

使 pm2 登录到控制台

如何以编程方式检测nodejs中的调试模式?

如何在express 中设置默认路径(路由前缀)?

Nodejs将字符串转换为UTF-8

AWS Lambda 函数写入 S3

deno vs ts-node:有什么区别