美文网首页web前端开发
NodeJS使用node-fetch下载文件并显示下载进度示例

NodeJS使用node-fetch下载文件并显示下载进度示例

作者: 关爱单身狗成长协会 | 来源:发表于2020-08-13 02:01 被阅读0次

    大晚上的睡不着,肚子又饿,吃了两包泡面后躺着又感觉太撑了,于是打开电脑随便看看电脑目录。结果发现了很早之前写的JS代码,那就顺便的分享给大家吧。

    我平时使用NodeJS写爬虫,下载文件一般都是使用node-fetch这个库

    最简单的下载文件,适用于小文件下载

    const fetch = require("node-fetch");
    const fs = require("fs");
    
    fetch("文件url",   {
            method: 'GET',
            headers: { 'Content-Type': 'application/octet-stream' },
    }).then(res => res.buffer()).then(_=>{
      fs.writeFile("文件保存路径", _, "binary", function (err) {
                    if (err) console.error(err);
                    else console.log("下载成功");
        });
    })
    

    下载较大的文件

    const fetch = require("node-fetch");
    const fs = require("fs");
    const path = require("path");
    const progressStream = require('progress-stream');
    
    //下载 的文件 地址
    let fileURL = "https://nodejs.org/dist/v12.18.3/node-v12.18.3-x64.msi";
    //下载保存的文件路径
    let fileSavePath = path.join(__dirname, path.basename(fileURL));
    //缓存文件路径
    let tmpFileSavePath = fileSavePath + ".tmp";
    //创建写入流
    const fileStream = fs.createWriteStream(tmpFileSavePath).on('error', function (e) {
        console.error('error==>', e)
    }).on('ready', function () {
        console.log("开始下载:", fileURL);
    }).on('finish', function () {
        //下载完成后重命名文件
        fs.renameSync(tmpFileSavePath, fileSavePath);
        console.log('文件下载完成:', fileSavePath);
    });
    //请求文件
    fetch(fileURL, {
        method: 'GET',
        headers: { 'Content-Type': 'application/octet-stream' },
        // timeout: 100,    
    }).then(res => {
        //获取请求头中的文件大小数据
        let fsize = res.headers.get("content-length");
        //创建进度
        let str = progressStream({
            length: fsize,
            time: 100 /* ms */
        });
        // 下载进度 
        str.on('progress', function (progressData) {
            //不换行输出
            let percentage = Math.round(progressData.percentage) + '%';
            console.log(percentage);
            // process.stdout.write('\033[2J'+);
            // console.log(progress);
            /*
            {
                percentage: 9.05,
                transferred: 949624,
                length: 10485760,
                remaining: 9536136,
                eta: 42,
                runtime: 3,
                delta: 295396,
                speed: 949624
            }
            */
        });
        res.body.pipe(str).pipe(fileStream);
    }).catch(e => {
        //自定义异常处理
        console.log(e);
    });
    
    下载效果

    断点续传

    之前我写过一篇文章 NodeJS实现简单的HTTP文件断点续传下载功能

    这里我根据原理将下载代码进行修改

    const fetch = require("node-fetch");
    const fs = require("fs");
    const path = require("path");
    const progressStream = require('progress-stream');
    
    //下载 的文件 地址
    let fileURL = "https://npm.taobao.org/mirrors/node/v14.8.0/node-v14.8.0-x64.msi";
    //下载保存的文件路径
    let fileSavePath = path.join(__dirname, path.basename(fileURL));
    //缓存文件路径
    let tmpFileSavePath = fileSavePath + ".tmp";
    //下载进度信息保存文件
    let cfgFileSavePath = fileSavePath + ".cfg.json";
    
    let downCfg = {
        rh: {},//请求头
        percentage: 0,//进度
        transferred: 0,//已完成
        length: 0,//文件大小
        remaining: 0,//剩余
        first: true//首次下载
    };
    let tmpFileStat = { size: 0 };
    //判断文件缓存 与 进度信息文件是否存在 
    if (fs.existsSync(tmpFileSavePath) && fs.existsSync(cfgFileSavePath)) {
        tmpFileStat = fs.statSync(tmpFileSavePath);
        downCfg = JSON.parse(fs.readFileSync(cfgFileSavePath, 'utf-8').trim());
        downCfg.first = false;
        //设置文件
        downCfg.transferred = tmpFileStat.size;
    }
    
    //创建写入流
    let writeStream = null;
    
    //请求头
    let fetchHeaders = {
        'Content-Type': 'application/octet-stream',
        "Cache-Control": "no-cache",
        Connection: "keep-alive",
        Pragma: "no-cache",
    };
    //追加请求范围
    if (downCfg.length != 0) {
        fetchHeaders.Range = "bytes=" + downCfg.transferred + "-" + downCfg.length;//71777113
    }
    if (downCfg.rh["last-modified"]) {
        fetchHeaders["last-modified"] = downCfg.rh["last-modified"];
    }
    //校验文件头
    const checkHerder = [
        "last-modified",//文件最后修改时间
        "server",//服务器
        // "content-length",//文件大小
        "content-type",//返回类型
        "etag",//文件标识
    ];
    
    fetch(fileURL, {
        method: 'GET',
        headers: fetchHeaders,
        // timeout: 100,    
    }).then(res => {
        let h = {};
        res.headers.forEach(function (v, i, a) { h[i.toLowerCase()] = v; });
        // console.log(h);
        //文件是否发生变化
        let fileIsChange = false;
        //是否首次下载
        if (downCfg.first) {
            //记录相关信息
            for (let k of checkHerder) downCfg.rh[k] = h[k];
            downCfg.length = h["content-length"];
        } else {
            //比较响应变化
            for (let k of checkHerder) {
                if (downCfg.rh[k] != h[k]) {
                    fileIsChange = true;
                    break;
                }
            }
            //是否运行范围下载
            downCfg.range = res.headers.get("content-range") ? true : false;
        }
        //创建文件写入流
        writeStream = fs.createWriteStream(tmpFileSavePath, { 'flags': !downCfg.range || fileIsChange ? 'w' : 'a' })
            .on('error', function (e) {
                console.error('error==>', e)
            }).on('ready', function () {
                console.log("开始下载:", fileURL);
            }).on('finish', function () {
                //下载完成后重命名文件
                fs.renameSync(tmpFileSavePath, fileSavePath);
                fs.unlinkSync(cfgFileSavePath);
                console.log('文件下载完成:', fileSavePath);
            });
    
        //写入信息文件
        fs.writeFileSync(cfgFileSavePath, JSON.stringify(downCfg));
        //获取请求头中的文件大小数据
        let fsize = h["content-length"];
        //创建进度
        let str = progressStream({
            length: fsize,
            time: 200 /* ms */
        });
        //创建进度对象
        str.on('progress', function (progressData) {
            //不换行输出
            let percentage = Math.round(progressData.percentage) + '%';
            console.log(percentage);
            //     console.log(`
            //     进度 ${progressData.percentage}
            //     已完成 ${progressData.transferred}
            //     文件大小 ${progressData.length}
            //     剩余 ${progressData.remaining}
            //         ${progressData.eta}
            //     运行时 ${progressData.runtime}
            //         ${ progressData.delta}
            //    速度 ${ progressData.speed}
            //             `);
            // console.log(progress);
            /*
            {
                percentage: 9.05,
                transferred: 949624,
                length: 10485760,
                remaining: 9536136,
                eta: 42,
                runtime: 3,
                delta: 295396,
                speed: 949624
            }
            */
        });
        res.body.pipe(str).pipe(writeStream);
        res.headers.forEach(function (v, i, a) {
            console.log(i + " : " + v);
        })
    }).catch(e => {
        //自定义异常处理
        console.log(e);
    });
    

    下载效果

    相关文章

      网友评论

        本文标题:NodeJS使用node-fetch下载文件并显示下载进度示例

        本文链接:https://www.haomeiwen.com/subject/hcbvdktx.html