亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在NodeJS中利用Range請求實現一個下載功能

發布時間:2021-03-08 14:46:16 來源:億速云 閱讀:285 作者:Leah 欄目:web開發

怎么在NodeJS中利用Range請求實現一個下載功能?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

服務端的實現

通過 http 模塊創建服務器處理 Range 請求,在服務器代碼中我們為了減少回調嵌套使用 async 函數,所以需要將異步的操作方法轉換成 Promise,以往我們使用 util 的 promisify 來一個一個轉換異步方法,比較麻煩,我們這次使用第三方模塊 mz 并直接引入轉換好的替代模塊。

使用 mz 之前需要先安裝:

npm install mz

服務端代碼如下:

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

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

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

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

  // 存在 range 請求頭將返回范圍請求的數據
  if (range) {
    // 獲取范圍請求的開始和結束位置
    let [, start, end] = range.match(/(\d*)-(\d*)/);

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

    // 文件總字節數
    let total = statObj.size;

    // 處理請求頭中范圍參數不傳的問題
    start = start ? ParseInt(start) : 0;
    end = end ? ParseInt(end) : total - 1;

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

// 創建服務器
const server = http.createServer(listener);

// 監聽端口
server.listen(3000, () => {
  console.log("server start 3000");
});

在上面服務端的代碼中,需要兼容 Range 請求和普通請求,兩種請求的區別是,如果客戶端發送的是 Range 請求,會攜帶 Range:bytes=0-5 格式的請求頭,我們可以通過 req 的 headers 屬性獲取,在獲取請求頭時,原本大寫字母開頭 NodeJS 統一處理成小寫,所以獲取時應小寫。

如果是 Range 請求則通過可讀流讀取對應的內容返回客戶端,如果不是,則通過可讀流讀取整個文件返回客戶端,在響應 Range 請求的過程中需要設置響應狀態為 206,需要設置響應頭 Accept-Ranges 值為 bytes,需要設置響應頭 Content-Range 值為 byte 0-5/100 的格式,0 為返回數據開始的索引,5 為結束的索引(包含),100 為文件的總字節數。

在通過 url 和 path 模塊解析和拼接下載文件路徑時,應該進行錯誤檢測,如果文件不存在則直接返回客戶端 Not Found。

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

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

客戶端的實現

在上面使用 curl 命令來訪問我們的服務器時,只能請求固定范圍的數據,而不是類似于下載功能,每次都下載一個范圍的數據,但是想要多次下載并自動維護 Range 的范圍需要借助我們自己實現的客戶端邏輯。

為了簡便,我們的下載客戶端是在命令行窗口運行的,通過指令來模擬實際項目中的開始下載、暫停和恢復按鈕,當在窗口中輸入 s 指令時開始下載,輸入 p 指令時暫停下載,輸入 r 指令時恢復下載。

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

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

let start = 0; // 請求初始值
let step = 5; // 每次請求字符個數
let pause = false; // 暫停狀態
let total; // 文件總長度

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

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

  // 維護下次 start 的值
  start += step;

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

    }

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

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

// 監控輸入
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;
  }
});

在上面代碼中下載的文件通過 config 中的 path 屬性配置,每次調用 download 函數下載時都會重新計算當前范圍請求的初始位置和結束位置,并設置 Range 請求頭,下一次請求靠遞歸 download 來實現。

在執行時需先啟動我們的服務器,在通過命令行輸入 node client.js 來啟動客戶端,在命令窗口輸入對應的指令進行開始下載、暫停下載和恢復下載操作。

關于怎么在NodeJS中利用Range請求實現一個下載功能問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宜君县| 淳化县| 宝应县| 油尖旺区| 肃宁县| 赤峰市| 永年县| 青海省| 张家界市| 桑植县| 罗源县| 大名县| 长垣县| 离岛区| 泰州市| 成都市| 同德县| 津南区| 海口市| 宣武区| 磐石市| 连城县| 罗山县| 邓州市| 渑池县| 宜丰县| 偃师市| 莒南县| 林州市| 浮梁县| 和龙市| 南京市| 枣强县| 正定县| 施甸县| 宝兴县| 罗山县| 温泉县| 横峰县| 新民市| 商丘市|