欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

NodeJS使用Range請(qǐng)求實(shí)現(xiàn)下載功能的方法示例

 更新時(shí)間:2018年10月12日 10:49:33   作者:PandaShen  
本篇使用 NodeJS 的 HTTP 服務(wù)創(chuàng)建客戶端,使用 Range 請(qǐng)求實(shí)現(xiàn)下載功能,本篇使用 NodeJS 的 HTTP 服務(wù)創(chuàng)建客戶端,使用 Range 請(qǐng)求實(shí)現(xiàn)下載功能

前言

本篇使用 NodeJS 的 HTTP 服務(wù)創(chuàng)建客戶端,使用 Range 請(qǐng)求實(shí)現(xiàn)下載功能,并通過(guò)本篇的 Demo 擴(kuò)展在業(yè)務(wù)中實(shí)現(xiàn)斷點(diǎn)續(xù)傳等功能的思路。

服務(wù)端的實(shí)現(xiàn)

我們通過(guò) http 模塊創(chuàng)建服務(wù)器處理 Range 請(qǐng)求,在服務(wù)器代碼中我們?yōu)榱藴p少回調(diào)嵌套使用 async 函數(shù),所以需要將異步的操作方法轉(zhuǎn)換成 Promise,以往我們使用 util 的 promisify 來(lái)一個(gè)一個(gè)轉(zhuǎn)換異步方法,比較麻煩,我們這次使用第三方模塊 mz 并直接引入轉(zhuǎn)換好的替代模塊。

使用 mz 之前需要先安裝:

npm install mz

服務(wù)端代碼如下:

// 文件:server.js
const http = require("http");
const path = require("path");
const url = require("url");

// 引入 mz 模塊轉(zhuǎn)換成 Promise 的 fs 模塊
const fs = require("mz/fs");

// 請(qǐng)求處理函數(shù)
async function listener(req, res) {
  // 獲取 range 請(qǐng)求頭,格式為 Range:bytes=0-5
  let range = req.headers["range"];

  // 下載文件路徑
  let p = path.resovle(__dirname, url.parse(url, true).pathname);

  // 存在 range 請(qǐng)求頭將返回范圍請(qǐng)求的數(shù)據(jù)
  if (range) {
    // 獲取范圍請(qǐng)求的開(kāi)始和結(jié)束位置
    let [, start, end] = range.match(/(\d*)-(\d*)/);

    // 錯(cuò)誤處理
    try {
      let statObj = await fs.stat(p);
    } catch (e) {
      res.end("Not Found");
    }

    // 文件總字節(jié)數(shù)
    let total = statObj.size;

    // 處理請(qǐng)求頭中范圍參數(shù)不傳的問(wèn)題
    start = start ? ParseInt(start) : 0;
    end = end ? ParseInt(end) : total - 1;

    // 響應(yīng)客戶端
    res.statusCode = 206;
    res.setHeader("Accept-Ranges", "bytes");
    res.setHeader("Content-Range", `bytes ${start}-${end}/${total}`);
    fs.createReadStream(p, { start, end }).pipe(res);
  } else {
    // 沒(méi)有 range 請(qǐng)求頭時(shí)將整個(gè)文件內(nèi)容返回給客戶端
    fs.createReadStream(p).pipe(res);
  }
}

// 創(chuàng)建服務(wù)器
const server = http.createServer(listener);

// 監(jiān)聽(tīng)端口
server.listen(3000, () => {
  console.log("server start 3000");
});

在上面服務(wù)端的代碼中,需要兼容 Range 請(qǐng)求和普通請(qǐng)求,兩種請(qǐng)求的區(qū)別是,如果客戶端發(fā)送的是 Range 請(qǐng)求,會(huì)攜帶 Range:bytes=0-5 格式的請(qǐng)求頭,我們可以通過(guò) req 的 headers 屬性獲取,在獲取請(qǐng)求頭時(shí),原本大寫字母開(kāi)頭 NodeJS 統(tǒng)一處理成小寫,所以獲取時(shí)應(yīng)小寫。

如果是 Range 請(qǐng)求則通過(guò)可讀流讀取對(duì)應(yīng)的內(nèi)容返回客戶端,如果不是,則通過(guò)可讀流讀取整個(gè)文件返回客戶端,在響應(yīng) Range 請(qǐng)求的過(guò)程中需要設(shè)置響應(yīng)狀態(tài)為 206,需要設(shè)置響應(yīng)頭 Accept-Ranges 值為 bytes,需要設(shè)置響應(yīng)頭 Content-Range 值為 byte 0-5/100 的格式,0 為返回?cái)?shù)據(jù)開(kāi)始的索引,5 為結(jié)束的索引(包含),100 為文件的總字節(jié)數(shù)。

在通過(guò) url 和 path 模塊解析和拼接下載文件路徑時(shí),應(yīng)該進(jìn)行錯(cuò)誤檢測(cè),如果文件不存在則直接返回客戶端 Not Found。

我們可以使用 curl 命令來(lái)檢測(cè)我們的服務(wù)端代碼,在命令行工具中輸入下面命令,在命令窗口查看返回值是否正確。

curl -v --header "Range:bytes=0-5" http://localhost:3000

客戶端的實(shí)現(xiàn)

在上面使用 curl 命令來(lái)訪問(wèn)我們的服務(wù)器時(shí),只能請(qǐng)求固定范圍的數(shù)據(jù),而不是類似于下載功能,每次都下載一個(gè)范圍的數(shù)據(jù),但是想要多次下載并自動(dòng)維護(hù) Range 的范圍需要借助我們自己實(shí)現(xiàn)的客戶端邏輯。

為了簡(jiǎn)便,我們的下載客戶端是在命令行窗口運(yùn)行的,通過(guò)指令來(lái)模擬實(shí)際項(xiàng)目中的開(kāi)始下載、暫停和恢復(fù)按鈕,當(dāng)在窗口中輸入 s 指令時(shí)開(kāi)始下載,輸入 p 指令時(shí)暫停下載,輸入 r 指令時(shí)恢復(fù)下載。

// 文件:client.js
const http = require("http");
const fs = require("fs");
const path = require("path");

// 請(qǐng)求配置
let config = {
  host: "localhost",
  port: 3000,
  path: "/download.txt"
};

let start = 0; // 請(qǐng)求初始值
let step = 5; // 每次請(qǐng)求字符個(gè)數(shù)
let pause = false; // 暫停狀態(tài)
let total; // 文件總長(zhǎng)度

// 創(chuàng)建可寫流
let ws = fs.createWriteStream(path.resolve(__dirname, config.path.slice(1)));

// 下載函數(shù)
function download() {
  // 配置,每次范圍請(qǐng)求 step 個(gè)字節(jié)
  config.headers = {
    "Range": `bytes=${start}-${start + step - 1}`;
  };

  // 維護(hù)下次 start 的值
  start += step;

  // 發(fā)送請(qǐng)求
  http.request(config, res => {
    // 獲取文件總長(zhǎng)度
    if (typeof total !== "number") {
      total = res.headers["content-ranges"].match(/\/(\d*)/)[1];

    }

    // 讀取返回?cái)?shù)據(jù)
    let buffers = [];
    res.on("data", data => buffers.push(data));
    res.on("end", () => {
      // 合并數(shù)據(jù)并寫入文件
      let buf = Buffer.concat(buffers);
      ws.write(buf);

      // 遞歸進(jìn)行下一次請(qǐng)求
      if (!pause && start < total) {
        download();
      }
    });
  }).end();
}

// 監(jiān)控輸入
process.stdin.on("data", data => {
  // 獲取指令
  let ins = data.toString().match(/(\w*)\/r/)[1];
  switch (ins) {
    case "s":
    case "r":
      pause = false;
      download();
      break;
    case "p":
      pause = true;
      break;
  }
});

在上面代碼中下載的文件通過(guò) config 中的 path 屬性配置,每次調(diào)用 download 函數(shù)下載時(shí)都會(huì)重新計(jì)算當(dāng)前范圍請(qǐng)求的初始位置和結(jié)束位置,并設(shè)置 Range 請(qǐng)求頭,下一次請(qǐng)求靠遞歸 download 來(lái)實(shí)現(xiàn)。

在執(zhí)行時(shí)需先啟動(dòng)我們的服務(wù)器,在通過(guò)命令行輸入 node client.js 來(lái)啟動(dòng)客戶端,在命令窗口輸入對(duì)應(yīng)的指令進(jìn)行開(kāi)始下載、暫停下載和恢復(fù)下載操作。

總結(jié)

相信現(xiàn)在已經(jīng)了解什么是范圍請(qǐng)求,范圍請(qǐng)求客戶端和服務(wù)端需要做些什么,其實(shí)說(shuō)白了就是對(duì)應(yīng)的請(qǐng)求頭和響應(yīng)頭的使用,需要注意的是范圍請(qǐng)求的響應(yīng)狀態(tài)碼為 206,這樣的需求在一些上傳、下載資源的網(wǎng)站也很常見(jiàn),其目的就是為了讓我們實(shí)現(xiàn)斷點(diǎn)續(xù)傳,不至于一次沒(méi)有上傳或下載完成的資源文件,在下一次的做同樣操作時(shí)需要重新來(lái)過(guò),可以接著上次的位置繼續(xù),范圍請(qǐng)求在視頻網(wǎng)站上也廣泛應(yīng)用,邊請(qǐng)求邊觀看,不至于一次加載整個(gè)視頻資源,節(jié)省流量,節(jié)省時(shí)間。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • nodejs實(shí)現(xiàn)的簡(jiǎn)單web服務(wù)器功能示例

    nodejs實(shí)現(xiàn)的簡(jiǎn)單web服務(wù)器功能示例

    這篇文章主要介紹了nodejs實(shí)現(xiàn)的簡(jiǎn)單web服務(wù)器功能,結(jié)合實(shí)例形式分析了nodejs構(gòu)建web服務(wù)器的相關(guān)監(jiān)聽(tīng)、響應(yīng)、數(shù)據(jù)處理等操作技巧,需要的朋友可以參考下
    2018-03-03
  • Lua表達(dá)式和控制結(jié)構(gòu)學(xué)習(xí)筆記

    Lua表達(dá)式和控制結(jié)構(gòu)學(xué)習(xí)筆記

    這篇文章主要介紹了Lua表達(dá)式和控制結(jié)構(gòu)學(xué)習(xí)筆記,本文講解了算術(shù)操作符、關(guān)系操作符、邏輯操作符、局部變量與作用域、控制結(jié)構(gòu)等內(nèi)容,需要的朋友可以參考下
    2014-12-12
  • 多版本node的安裝和切換詳細(xì)操作步驟

    多版本node的安裝和切換詳細(xì)操作步驟

    有時(shí)候需要運(yùn)行不同的項(xiàng)目,node版本不一致會(huì)導(dǎo)致不少問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于多版本node的安裝和切換詳細(xì)操作步驟的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • 深入解析koa之異步回調(diào)處理

    深入解析koa之異步回調(diào)處理

    這篇文章主要介紹了深入解析koa之異步回調(diào)處理,我們研究一下koa當(dāng)中異步回調(diào)同步化寫法的原理,同樣的,我們也會(huì)實(shí)現(xiàn)一個(gè)管理函數(shù),是的我們能夠通過(guò)同步化的寫法來(lái)寫異步回調(diào)函數(shù)。,需要的朋友可以參考下
    2019-06-06
  • node.js cookie-parser之parser.js

    node.js cookie-parser之parser.js

    這篇文章主要介紹node.js cookie-parser之parser.js,講解的比較詳細(xì),需要的朋友可以參考下。
    2016-06-06
  • webpack創(chuàng)建項(xiàng)目并打包的詳細(xì)流程記錄

    webpack創(chuàng)建項(xiàng)目并打包的詳細(xì)流程記錄

    webpack在前端工程領(lǐng)域起到了中流砥柱的作用,理解它的內(nèi)部實(shí)現(xiàn)機(jī)制會(huì)對(duì)你的工程建設(shè)提供很大的幫助(不論是定制功能還是優(yōu)化打包),下面這篇文章主要給大家介紹了關(guān)于webpack創(chuàng)建項(xiàng)目并打包的詳細(xì)流程,需要的朋友可以參考下
    2023-03-03
  • Nodejs進(jìn)階:基于express+multer的文件上傳實(shí)例

    Nodejs進(jìn)階:基于express+multer的文件上傳實(shí)例

    本篇文章主要介紹了基于express+multer的文件上傳實(shí)例,現(xiàn)在分享給大家,也給大家做個(gè)參考,感興趣的小伙伴們可以參考一下。
    2016-11-11
  • 詳解node中創(chuàng)建服務(wù)進(jìn)程

    詳解node中創(chuàng)建服務(wù)進(jìn)程

    本篇文章主要介紹了詳解node中創(chuàng)建服務(wù)進(jìn)程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • node通過(guò)npm寫一個(gè)cli命令行工具

    node通過(guò)npm寫一個(gè)cli命令行工具

    本篇文章主要介紹了node通過(guò)npm寫一個(gè)cli命令行工具 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • nodejs讀取圖片返回給瀏覽器顯示

    nodejs讀取圖片返回給瀏覽器顯示

    這篇文章主要為大家詳細(xì)介紹了nodejs讀取圖片返回給瀏覽器顯示,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07

最新評(píng)論