斷點續傳瞭解一下啊?

是否是被圖片騙進來了?哈哈不要着急走,其實也不是跟圖不要緊,咱們此次就是說斷點續傳的那些事。
咱們寫代碼的有時候工做中會作一些上傳/下載圖片或者文件的操做。萬一線路中斷,不具有斷點續傳的 HTTP/FTP 服務器或下載軟件就只能從頭重傳,比較好的 HTTP/FTP 服務器或下載軟件具備斷點續傳能力,容許用戶從上傳/下載斷線的地方繼續傳送,這樣大大減小了用戶的煩惱。
複製代碼

獲取部份內容的範圍請求

其實啊,爲了實現中斷恢復下載的需求,須要能下載指定下載的實體範圍:node

  • 請求頭中的Range來指定 資源的byte範圍
  • 響應會返回狀態碼206響應報文
  • 對於多重範圍的範圍請求,響應會在首部字段Content-Type中標明multipart/byteranges 咱們來模擬一下: 先打開咱們的命令行工具,寫上以下代碼 (後面是我在百度上找的一個圖片)
curl -v --header "Range:bytes=0-10" http://pic26.nipic.com/20130117/10558908_124756651000_2.jpg
複製代碼

運行一下獲得的這個結果:promise

從http那段服務器返回給咱們的信息能夠看出以下信息(206表明服務器答應咱們的range請求方式)緩存

  • Content-Type: image/jpeg 類型是圖片
  • Accept-Ranges: bytes 單位是bytes
  • Content-Range: bytes 0-10/292871 請求段爲0-10,總共有292871bytes
  • Content-Length: 11 請求長度爲11 知道這些以後咱們能夠簡單模擬一下客戶端和服務器端怎麼作的:

服務器端代碼

首先客戶端要發一個頭Range:bytes=0-10,而後服務端返回一個頭 // Accept-Ranges:bytes // Content-Range:0-10/總大小bash

// 獲取範圍請求
let http = require('http');
let fs = require('fs');
let path = require('path');
let { promisify } = require('util');//這個模塊是包裝某個東西成爲promise的。
let stat = promisify(fs.stat);

let server = http.createServer(async function (req, res) {

    let p = path.join(__dirname, 'con.txt');
    // 判斷當前文件的大小
    let statObj = await stat(p);
    let start = 0;
    let end = statObj.size - 1; // 讀流是包前又包後的:statObj.size 是文件的大小。
    let total = end
    let range = req.headers['range'];
    if (range) {//判斷客戶端是否有帶range的請求
        // 告訴它支持範圍請求
        res.setHeader('Accept-Ranges','bytes');
        // ['匹配的字符串','第一個分組']
        let result = range.match(/bytes=(\d*)-(\d*)/);
        start = result[1]?parseInt(result[1]):start;
        end = result[2]?parseInt(result[2])-1:end;
        // 獲取成功而且文件總大小是多少
        // Content-Range:0-10/總大小
        res.setHeader('Content-Range',`${start}-${end}/${total}`)
    }
    res.setHeader('Content-Type', 'text/plain;charset=utf8');
    fs.createReadStream(p, { start, end }).pipe(res);
});
server.listen(3000);
複製代碼

這樣一個簡單的服務器端就寫好了。服務器

客戶端代碼

//首先要寫一個options 
let options = {
    hostname:'localhost',
    port:3000,
    path:'/',
    method:'GET'
}
let fs = require('fs');
let path = require('path');
let http = require('http');
let ws = fs.createWriteStream('./download.txt');
let pause = false;
let start = 0;
// 下載,每次獲取10個
process.stdin.on('data',function(chunk){
    chunk = chunk.toString();
    if(chunk.includes('p')){  //輸入p就是暫停
        pause = true
    }else{
        pause = false;
        download();
    }
});
function download(){
    options.headers = {
        Range:`bytes=${start}-${start+10}`
    }
    start+=10;
    // 發請求
    http.get(options,function(res){
        let range = res.headers['content-range'];
        let total = range.split('/')[1];
        let buffers = [];//建立一個緩存區,把讀到的數據都放在裏面
        //,等到end的時候就整個取出來。
        res.on('data',function(chunk){
            buffers.push(chunk);
        });
        res.on('end',function(){
            //將獲取的數據寫入到文件中
            ws.write(Buffer.concat(buffers));
            setTimeout(function(){
                if(pause === false&&start<total){
                    download();
                }   
            },1000)
        })
    })
}
download();
複製代碼

代碼很是淺顯易懂。 最後咱們在同級目錄下建立一個con.txt文件。用node執行一下客戶端文件,就會實現功能啦。網絡

場景校驗

有時候即便終端發起續傳請求是url對應問問價在服務器端已經發生變化,那麼很顯然此時須要一個標識問價惟一的方式,curl

  • Etag(請求頭) ===> if-none-match
  • Last-Modified(請求頭) ===> if-modified-since

Etag

Etag(Entity Tags)主要爲了解決 Last-Modified 沒法解決的一些問題。async

  • 一些文件也許會週期性的更改,可是內容並不改變(僅改變修改時間),這時候咱們並不但願客戶端認爲這個文件被修改了,而從新 GET。
  • 某些文件修改很是頻繁,例如:在秒如下的時間內進行修改(1s 內修改了 N 次),If-Modified-Since 能檢查到的粒度是 s 級的,這種修改沒法判斷(或者說 UNIX 記錄 MTIME 只能精確到秒)。
  • 某些服務器不能精確的獲得文件的最後修改時間。

爲此,HTTP/1.1 引入了 Etag。Etag 僅僅是一個和文件相關的標記,能夠是一個版本標記,例如:v1.0.0;或者說 「627-4d648041f6b80」 這麼一串看起來很神祕的編碼。可是 HTTP/1.1 標準並無規定 Etag 的內容是什麼或者說要怎麼實現,惟一規定的是 Etag 須要放在 「」 內,通常狀況下Etag的值是時間+文件大小工具

Last-Modified

If-Modified-Since,和 Last-Modified 同樣都是用於記錄頁面最後修改時間的 HTTP 頭信息,只是 Last-Modified 是由服務器往客戶端發送的 HTTP 頭,而 If-Modified-Since 則是由客戶端往服務器發送的頭,能夠看到,再次請求本地存在的 cache 頁面時,客戶端會經過 If-Modified-Since 頭將先前服務器端發過來的 Last-Modified 最後修改時間戳發送回去,這是爲了讓服務器端進行驗證,經過這個時間戳判斷客戶端的頁面是不是最新的,若是不是最新的,則返回新的內容,若是是最新的,則返回 304 告訴客戶端其本地 cache 的頁面是最新的,因而客戶端就能夠直接從本地加載頁面了,這樣在網絡上傳輸的數據就會大大減小,同時也減輕了服務器的負擔。 若是想了解具體怎麼實現能夠看看靜態服務裏面的緩存。post

是否是躍躍欲試??

若是對你有幫助請點個贊噻!

相關文章
相關標籤/搜索