前後端交互一(ajax基本使用,XMLHttpRequest對象實現數據交互,onreadystatechange服務器響應信息,FormData對象上傳文件,upload事件對象)

目錄

1.重點掌握及知識要點

2.登錄簡單回顧

3.利用ajax來解決驗證用戶名問題

3.1ajax的基本使用

3.2案例實現通過ajax異步無刷新驗證用戶名

4.針對ajax的詳細解釋

4.1get注意點

4.1.1get傳參的兩種方式:queryString和/get/id

4.1.2queryString傳參方式

4.1.3/get/id傳參方式

4.2post傳參方式

4.2.1post傳參方式注意點

4.2.2post傳參方式設置http正文頭格式

4.2.3onload()方法中獲取返還頭部信息

4.2.4完整案例

4.3同步及異步ajax

4.4onload()

5.onreadystatechange——瞭解

5.1onreadystatechange

5.2readyState

5.3status常用狀態碼

5.4onreadystatechange示例: 

5.5onload實現onreadystatechange功能

6.返還數據類型(XML,Json) 

6.1獲取原始數據xhr.response

6.2返還數據爲json時xhr.responseText

6.3返還數據爲json時,xhr.responseXML來獲取 

7.利用FormData對象來實現文件上傳

7.1 創建FormData對象

7.2 監控上傳進度——upload 事件

7.2.1upload事件下的各種事件——事件鉤子

7.2.2監控文件上傳進度示例

8.回顧


1.重點掌握及知識要點

## 重點掌握

  1. 理解ajax基本使用
  2. 會使用XMLHttpRequest對象實現數據交互
  3. 瞭解onreadystatechange服務器響應信息(狀態碼)
  4. 會使用FormData對象上傳文件
  5. 瞭解upload事件對象(XMLHttpRequest下的事件對象)

## 知識要點

  1. ajax使用
  2. XMLHttpRequest對象
  3. FormData對象
  4. upload 事件對象

2.登錄簡單回顧

  1. 提出ajax驗證用戶名需求;
  2. 驗證錯誤後,如果通過跳轉解決很麻煩;
  3. 各種跳轉用戶體驗差

3.利用ajax來解決驗證用戶名問題

ajax是: Ajax即“Asynchronous Javascript And XML”(異步 JavaScript 和 XML)

3.1ajax的基本使用

  • 新建XMLHttpRequest對象;
 let xhr = new XMLHttpRequest();
  • 配置請求參數
 xhr.open("get","/checkUser",true); //true是異步,false是同步
  • 接收返還值
  xhr.onload = function(){
     let res = JSON.parse(xhr.responseText);
  }
  • 發送服務器請求
xhr.send();

3.2案例實現通過ajax異步無刷新驗證用戶名

注意:

  1. 通過 XMLHttpRequest對象中open()方法發起請求。xhr.open("get","/checkUser?username="+this.value,true); //true表示異步發送請求,false同步發送請求,默認爲false,所以必須設置爲true
  2. 通過 XMLHttpRequest對象中onload()方法獲取返還數據。注意返回數據是從XMLHttpRequest對象中獲得的,xhr.responseText(獲取json數據)和xhr.response(獲取原始數據),xhr.responseXML(獲取XML數據)都可以獲取返還數據
  3. 獲取到的數據是JSON格式的,需要將其轉爲對象。JSON.parse(xhr.responseText).msg
  4. 後臺接收queryString參數通過cxt.query接收;如果是post普通數據傳參只需引入koa-body模塊,後臺通過ctx.request.body接收;如果是post傳參且上傳文件需引入koa-bodyparser ,然後通過ctx.request.body接收

 login.css:

.loginContainer{
    margin: 0 auto;
    width: 600px;
    text-align: center;
    padding-top: 20px;
    padding-bottom: 50px;
    border: 1px solid;
}
.loginContainer input{
    margin-bottom: 20px;
}
.loginStyle{
    width: 160px;
    height: 40px;
    background: rgb(50,203,77);
    color: white;
    font-size: 17px;
}
.inputStyle{
    width: 200px;
    height: 30px;
    padding: 5px;
    outline: none;
}

.inputStyle:focus{
    border: 1px solid rgb(50,203,77);
}
form{
    position: relative;
}
.exchange{
    position: absolute;
    top:8px;
    right: 65px;
    color: red;
    display: none;
}

user.json:

[
    {
        "id":1,
        "username":"zhangsan",
        "pwd":"123"
    },{
        "id":2,
        "username":"lisi",
        "pwd":"123"
    }
]

 login.js:

const Koa = require("koa");
const Router = require("koa-router");
const static = require("koa-static");
// const views = require("koa-views");
const userData = require("./data/user.json");

let app = new Koa();
let router = new Router();

app.use(static(__dirname + "/static"));
//前面頁面直接放到static裏時,只能通過login.html訪問,不能通過/直接訪問
router.get("/checkUser", (ctx, next) => {
    // 注意接收queryString參數通過cxt.query獲得;如果是post傳參需引入koa-bodyparser ,然後通過ctx.request.body接收
    let username = userData.find(item=>item.username === ctx.query.username);
    console.log(ctx.query);
    if(username){
    // node.js會自動將對象轉爲json傳給前端,所以不用再進行轉換
        ctx.body = {
            status:1,
            msg:"用戶名正確"
        };
    }else{
        ctx.body = {
            status:0,
            msg:"用戶名錯誤"
        };
    }
});

app.use(router.routes());
app.listen("9090");

login.html:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <link rel="stylesheet" href="css/login.css" />
  <title>Document</title>
</head>

<body>
  <div class="loginContainer">
    <h1>登錄</h1>
    <form action="/checkUser" method="post">姓名:
      <input class="inputStyle" type="text" name="username" />
      <div class="exchange">用戶名錯誤</div>
      <br />密碼:
      <input class="inputStyle" type="password" name="pwd" /><br />
      <input class="loginStyle" type="submit" value="登錄" />
    </form>
  </div>
  <script>
{
  //鼠標失去焦點時,進行無刷新驗證
  let username = document.querySelectorAll("input");
  let exchange = document.querySelector(".exchange");
 //使用焦點時驗證用戶名和密碼
 
  username[0].onblur = function(){
    let xhr = new XMLHttpRequest();
    xhr.open("get","/checkUser?username="+this.value,true);//true表示異步發送請求,false同步發送請求
    xhr.onload = function() {
      //注意返回數據是從XMLHttpRequest對象中獲得的,xhr.responseText和xhr.response都可以獲取返還數據
      let res = JSON.parse(xhr.responseText);
      console.log(JSON.parse(xhr.responseText).msg);
      console.log(xhr.response);
      exchange.style.display = "block";
      //獲取到的數據是JSON格式的,需要將其轉爲對象
      exchange.innerHTML = JSON.parse(xhr.responseText).msg;
      console.log(res.status);
      
      if(res.status === 1){
        exchange.style.color = "green";
      }else{
        exchange.style.color = "red";
      }
    }
     // 必須調用send()方法
    xhr.send();
  }
}
  </script>
</body>

通過http://localhost:9090/login.html即可請求道login.html頁面 

4.針對ajax的詳細解釋

很多ajax框架都是基於XMLHttpRequest對象去實現的,但是很多框架也都存在一定問題,如axiso就不能實現文件或圖片上傳。

4.1get注意點

  • get通過parmas傳參
  • get和querystring的問題,通過url傳參

a標籤,img標籤中的src和script標籤中的src都是使用get請求

4.1.1get傳參的兩種方式:queryString和/get/id

  1. queryString,通過queryString傳參有長度限制,默認爲2048
  2. /get/id

傳參方式注意點:

  • querystring傳參和get/post請求方式是兩個概念,不是使用querystring傳參就是get請求,post也可以通過querystring進行傳參;通過ctx.query獲取傳遞過來的參數
  • 通過querystring方式進行傳參時,後臺接收地址不受影響;但如果使用/get/id方式傳參,後臺必須通過/get/:id方式接收參數,並通過ctx.params獲得參數值

4.1.2queryString傳參方式

如上例。

注意queryString傳參方式,通過ctx.query獲取傳遞過來的參數。

4.1.3/get/id傳參方式

傳參方式:‘/請求地址/參數’ 

接收參數:‘/請求地址/:參數’ 接收地址,使用ctx.params獲取參數值

<body>
    <button>點擊發送get請求</button>
    <script>
        {
            //通過/get/3的方式進行傳參,後臺通過/get/:id進行獲取,ctx.params得到具體參數值
            document.querySelector("button").onclick = function(){
                let xhr = new XMLHttpRequest();
                xhr.open("get","/getInfo/1");
                xhr.onload = function(){
                    console.log(xhr.responseText);//{"status":1,"msg":"請求成功"}
                }
                xhr.send();
            };
        }
    </script>
</body>

login.js:get請求(/get/id傳參方式)

//get請求(/get/id傳參方式)
router.get("getInfo","/getInfo/:id",ctx=>{
    console.log(ctx.params);//{ id: '1' }
    ctx.body = {
        status:1,
        msg:"請求成功"
    };
});

4.2post傳參方式

post傳參方式,如果需要發送數據,需要通過send(data)方法進行發送,發送給的數據是通過http正文頭髮送。

且post傳參方式,node.js後臺需要使用koa-body進行接收,ctx.request.body獲取參數。

注意:如果post請求方式通過queryString方式傳參,就需要通過ctx.query獲取參數。

4.2.1post傳參方式注意點

  • post一般不通過queryString進行傳參,因爲queryString傳參有長度限制(服務器會限制),默認2048。
  • 發送數據時候需要設置http正文頭格式:post傳參是通過HTTP正文進行傳參,正文傳參必須設置編碼格式。同form表單的默認編碼格式    <form action="" enctype="application/x-www-form-urlencoded"></form>。form表單中可以省略,但是ajax請求時不能省略。
  • 需要手動設置正文頭,setRequestHeader()的content-type爲json且發送的數據通過JSON.stringtify()進行處理,傳遞過去的數據node纔會以對象進行接收。如果沒有設置也可以接收,但是需要通過queryString模塊ctx.query進行處理,但是前端這樣傳遞數據不好。

注意點:

  1. 發送數據時候需要設置http正文頭格式;
  2. 獲取頭部信息:getAllResponseHeaders  或者是getResponseHeader ;

4.2.2post傳參方式設置http正文頭格式

發送數據時候需要設置http正文頭格式:

xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");  //默認編碼
xhr.setRequestHeader("Content-type","multipart/form-data");  //二進制編碼,上傳文件時使用
xhr.setRequestHeader("Content-type","application/json");  //json編碼:傳輸數據也需要時JSON格式

示例:注意使用post請求時,後臺必須使用koa-body模塊,才能獲取到參數

http正文頭格式爲默認編碼時

<body>
    <button>點擊發送post請求</button>
    <script>
        {
            //通過/get/3的方式進行傳參,後臺通過/get/:id進行獲取,ctx.params得到具體參數值
            document.querySelector("button").onclick = function(){
                let xhr = new XMLHttpRequest();
                xhr.open("post","/getPostInfo");
                xhr.onload = function(){
                    console.log(xhr.responseText);//{"status":1,"msg":"請求成功"}
                }
                //設置正文請求頭
                xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
                let data = "username=zs&age=20";
                xhr.send(data);
            };
        }
    </script>
</body>

http正文頭格式設置爲JSON時:如果正文頭是JSON格式,則傳輸數據也需要是json格式,必須使用JSON.stringify()將數據轉爲json格式

 //正文請求頭設置爲json時,傳輸數據也需要是JSON格式
                xhr.setRequestHeader("content-type","application/json; charset=utf-8");
                let data = JSON.stringify({
                    username:'zs',
                    age:12
                });

後臺獲取post參數:需要引入koa-body,然後通過ctx.request.body接收參數 

注意:ctx.body是ctx.response.body的別名,而ctx.request.body是post的傳參。

const koaBody = require("koa-body");
app.use(koaBody());
//post請求
router.post("/getPostInfo",ctx=>{
    console.log(ctx.request.body);//{ username: 'zs', age: '20' }
    
    ctx.body = {
        status:1,
        msg:"請求成功"
    };
});

4.2.3onload()方法中獲取返還頭部信息

getAllResponseHeaders()獲取所有頭部信息(某些屬性有可能會獲取不到,大部分都能獲取)  或者是getResponseHeader("請求頭屬性") 獲取某個頭部信息

                xhr.onload = function(){
                    console.log(xhr.responseText);//{"status":1,"msg":"請求成功"}
                    //獲取所有頭部信息
                    console.log(xhr.getAllResponseHeaders());
                    //獲取某個頭部信息
                    console.log(xhr.getResponseHeader("content-type"));//application/json; charset=utf-8
                }

4.2.4完整案例

ajax_post.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <button>點擊發送post請求</button>
    <script>
        {
            //通過/get/3的方式進行傳參,後臺通過/get/:id進行獲取,ctx.params得到具體參數值
            document.querySelector("button").onclick = function(){
                let xhr = new XMLHttpRequest();
                xhr.open("post","/getPostInfo");
                xhr.onload = function(){
                    console.log(xhr.responseText);//{"status":1,"msg":"請求成功"}
                    //獲取所有頭部信息
                    console.log(xhr.getAllResponseHeaders());
                    //獲取某個頭部信息
                    console.log(xhr.getResponseHeader("content-type"));
                }
                //設置正文請求頭
                // xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");//默認編碼
                // xhr.setRequestHeader("content-type","multipart/form-data");//二進制編碼,上傳文件時使用
                // let data = "username=zs&age=20";
                //正文請求頭設置爲json時,傳輸數據也需要是JSON格式
                xhr.setRequestHeader("content-type","application/json; charset=utf-8");//json編碼:傳輸數據也需要時JSON格式
                // 傳輸數據也需要是json格式,必須使用JSON.stringify()將數據轉爲json格式
                let data = JSON.stringify({
                    username:'zs',
                    age:12
                });
                xhr.send(data);
            };
        }
    </script>
</body>
</html>

login.js:

app.use(koaBody());
//post請求
router.post("/getPostInfo",ctx=>{
    console.log(ctx.request.body);//{ username: 'zs', age: '20' }
    
    ctx.body = {
        status:1,
        msg:"請求成功"
    };
});

 結果:所有頭部信息

date: Wed, 18 Sep 2019 01:59:50 GMT
connection: keep-alive
content-length: 33
content-type: application/json; charset=utf-8

4.3同步及異步ajax

  • 設置異步的請求不會影響其他請求或代碼的執行;
  • 當設置同步執行時,其後其他請求或代碼必須等該請求執行完後,纔會進行執行

異步:設置異步的請求不會影響其他請求或代碼的執行

<body>
    <button>按鈕一</button>
    <button>按鈕二</button>
    <script>
    {
        let btns = document.querySelectorAll("button");
        btns[0].onclick = function(){
            let xhr = new XMLHttpRequest();
            xhr.open("get","/getInfo/2",true);
            xhr.onload = function(){
                console.log(xhr.responseText);
            }
            xhr.send();
        }
        //點擊第二個按鈕時進行打印
        btns[1].onclick = function(){
            console.log("按鈕二打印。。。");
            
        }
    }
    </script>
</body>

結果:按鈕一的請求不會影響按鈕二的打印結果(測試時網速太快看不到效果,可以將Network中的online網絡改爲slow3G再測)

異步:當設置同步執行時,其他請求或代碼必須等該請求執行完後,纔會進行執行

 xhr.open("get","/getInfo/2",false);

 結果:永遠都只會先打印按鈕一發送的請求後再執行按鈕二的請求

4.4onload()

以前是使用onreadystatechange後,通過readyState的狀態判斷是否請求完畢,現在使用onload()更加簡潔方便。

onload()方法是異步的,即使寫在send()方法前面也會再send()方法執行後纔會執行,一般習慣上會將send()方法放在最後面。因爲很多東西必須在send()之前設置纔有用,如setRequestHeader()就必須在send()之前設置。

5.onreadystatechange——瞭解

5.1onreadystatechange

onreadystatechange:存有處理服務器響應的函數,每當 readyState 改變時,onreadystatechange 函數就會被執行

5.2readyState

readyState:存有服務器響應的狀態信息。

  • 0: 請求未初始化(代理被創建,但尚未調用 open() 方法)
  • 1: 服務器連接已建立(`open`方法已經被調用)
  • 2: 請求已接收(`send`方法已經被調用,並且頭部和狀態已經可獲得)
  • 3: 請求處理中(下載中,`responseText` 屬性已經包含部分數據)
  • 4: 請求已完成,且響應已就緒(下載操作已完成)

5.3status常用狀態碼

status常用狀態——http狀態碼
HTTP狀態碼 描述

100

繼續。繼續響應剩餘部分,進行提交請求

200

成功

301

永久移動。請求資源永久移動到新位置

302

臨時移動。請求資源零時移動到新位置

304

未修改。請求資源對比上次未被修改,響應中不包含資源內容

401

未授權,需要身份驗證

403

禁止。請求被拒絕

404

未找到,服務器未找到需要資源

500

服務器內部錯誤。服務器遇到錯誤,無法完成請求

503

服務器不可用。臨時服務過載,無法處理請求

5.4onreadystatechange示例: 

<body>
    <button>點擊</button>
    <script>
        document.querySelector("button").onclick = function(){
            let xhr = new XMLHttpRequest();
            xhr.open("get","/getInfo/3",true);
            xhr.onreadystatechange = function(){
                //判斷服務求響應狀態爲4和返還狀態200(成功)
                if(xhr.readyState == 4){
                    if(xhr.status == 200){
                        console.log(xhr.responseText);//{"status":1,"msg":"請求成功"}
                    }
                }
            }
            xhr.send();
        };
    </script>
</body>

5.5onload實現onreadystatechange功能

其實,使用onload也有服務器響應信息和返回狀態碼:建議使用onload會更加簡潔

xhr.onload = function(){
                console.log(xhr.readyState);//4
                console.log(xhr.status);//200
                console.log(xhr.responseText);//{"status":1,"msg":"請求成功"}
            }

6.返還數據類型(XML,JSON) 

前端和後臺之前,平臺和平臺之間數據傳輸都會使用同一的數據格式,現在主流的數據跨平臺交互有XML和JSON。

6.1獲取原始數據xhr.response

//獲取原始數據
console.log(xhr.response);

6.2返還數據爲json時xhr.responseText

  • 服務器返還json數據:xhr.responseText 來獲取
xhr.responseText  //來獲取

6.3返還數據爲json時,xhr.responseXML來獲取 

  • 服務器返還xml數據 :
  1. xhr.responseXML //獲取值
  2. 服務器端設置response裏的content-type內容ctx.set("content-type","text/html");
  3. 前端重寫XML(以防後臺沒有指定content-type類型):xhr.overrideMimeType('text/xml;charset=utf-8');注意屬性之間不能有空格
  4. 前端和服務器端隨便那邊設置返還數據格式即可,不需要都設置
xhr.responseXML //獲取值

 示例:

前端HTML:

<body>
    <button>點擊獲取XML數據</button>
    <script>
    {
        document.querySelector("button").onclick = function(){
            let xhr = new XMLHttpRequest();
            xhr.open("get","/getXMLInfo",true);
            //如果後臺沒有設置XML的content-type,前端就必須重寫格式
            xhr.overrideMimeType('text/xml;charset=utf-8');
            xhr.onload = function(){
                //獲取XML格式數據
                console.log(xhr.responseXML);
                console.log(xhr.responseXML.getElementsByTagName("name")[0]);
                //獲取原始數據
                console.log(xhr.response);
                
            }
            xhr.send();
        }
    }
    </script>
</body>

後臺:

//獲取XML數據
router.get("/getXMLInfo",ctx=>{
    ctx.set("content-type","text/xml");
    //注意這裏反引號和xml內容不能換行
    ctx.body = `<?xml version='1.0' encoding='utf-8' ?>
        <books>
            <nodejs>
                <name>nodeJS實戰</name>
                <price>52.0元</price>
            </nodejs>
            <react>
                <name>react進階</name>
                <price>56.0元</price>
            </react>
        </books>`;
});

 結果:

爲防止後臺沒有寫XML的content-type,需要在前端重寫XML格式:

xhr.overrideMimeType('text/xml;charset=utf-8');

7.利用FormData對象來實現文件上傳

7.1 創建FormData對象

注意點:

  1. <input type="file" class="myfile">通過files屬性獲取到的是類數組
  2. 通過new FormData()創建文件上傳對象;
  3.  formData.append(name,value);name必須和後臺接收時name保持一致,value可以是文件也可以是普通數據
  4. 文件上傳必須通過正文方式進行傳遞,所以必須使用post請求
  5. 使用FormData時會自動對content-type進行設置,就不需要再進行手動設置
  6. 後臺接收數據時,通過前端append()中的name屬性即可獲取到對應數據或文件
  7. 再通過fs模塊對相應文件進行轉存即可。ctx.request.files.img.path即文件的臨時路徑,對臨時路徑中的文件轉存到服務器下路徑即可
  8. 文件轉存時有可能出現文件夾權限問題,需要手動開啓權限。且文件夾不存在,需要先創建文件夾

示例:

前端:注意上傳文件是通過正文提交,所以必須使用post方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <input type="file" class="myfile">
    <button>點擊上傳</button>
    <script>
        document.querySelector("button").onclick = function(){
            let myfile = document.querySelector(".myfile");
            //files屬性返回的是類數組
            let files = myfile.files;
            
            //創建FormData對象,進行上傳(使用FormData後,就不需要在表單中使用form-data進行上傳)
            let formData = new FormData();
            //img表示name,相當於form表單中的name屬性,file[0]表示要上傳的單個文件
            formData.append("img",files[0]);
            //其他的數據,也可以進行傳輸
            formData.append("username","張三");

            //文件上傳必須通過正文方式進行傳遞,所以必須使用post請求
            //使用FormData時會自動對content-type進行設置,就不需要再進行手動設置
            let xhr = new XMLHttpRequest();
            xhr.open("post","/upload",true);
            xhr.onload = function(){
                console.log(xhr.responseText);
            };
            // 沒選擇文件需要提示,否則會報錯
            if(files>0){
                xhr.send(formData);
            }else{
                alert("請選擇文件");
            }
        };
    </script>
</body>
</html>

後臺:獲取上傳文件必須使用mutipart:ture;通過ctx.request.files.img獲取文件

const Koa = require("koa");
const Router = require("koa-router");
const static = require("koa-static");
const koaBody = require("koa-body");
const fs = require("fs");

let app = new Koa();
let router = new Router();
app.use(static(__dirname + "/static"));
//上傳文件時,必須設置允許文件上傳,否則接收不了
app.use(koaBody({
    multipart:true
}));

//上傳文件
router.post("/upload",ctx=>{
    //通過前端append()中的name屬性即可獲取到對應數據或文件
    // console.log(ctx.request.body);//{ username: '張三' }
    // console.log(ctx.request.files.img);
    //通過fs模塊對相應文件進行轉存即可
    //ctx.request.files.img.path即文件的臨時路徑,對臨時路徑中的文件轉存到服務器下路徑即可
    let fileData = fs.readFileSync(ctx.request.files.img.path);
    //文件轉存時有可能出現文件夾權限問題,需要手動開啓權限
    //判斷文件夾不存在,需要先創建文件夾
    if(!fs.existsSync("static/imgs")){
        fs.mkdirSync("static/imgs/");
    }
    fs.writeFileSync("static/imgs/"+ctx.request.files.img.name,fileData);
    
    ctx.body = {
        status:1,
        msg:"文件上傳成功"
    };
});

app.use(router.routes());
app.listen("8888");

 結果:{"status":1,"msg":"文件上傳成功"}

7.2 監控上傳進度——upload 事件

7.2.1upload事件下的各種事件——事件鉤子

以下事件都是在upload事件下:

  • onloadstart   上傳開始
  • onprogress  數據傳輸進行中(evt.total :需要傳輸的總大小;evt.loaded :當前上傳的文件大小;
  • onabort 上傳操作終止(取消上傳xhr.abort())
  • onerror  上傳失敗
  • onload 上傳成功
  • onloadend 上傳完成(不論成功與否)

7.2.2監控文件上傳進度示例

注意點:

  • 使用<process>標籤可以顯示文件上傳進度
  • 監控文件上傳速度(需要onloadstart和onprogress的時間差,及時間差內文件已上傳的文件大小),當前文件大小/時間差 即文件上傳速度;時間差需要轉爲單位爲秒
  • evt.total :需要傳輸的總大小;evt.loaded :當前上傳的文件大小
  • 需要處理上傳的單位b/s ,判斷文件足夠大時使用kb/s;

文件上傳頁面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
    .progressSpan{
        display: none;
        color: seagreen;
    }

    </style>
</head>
<body>
    <input type="file" class="myfile">
    進度:<progress value="0" max="100"></progress>&nbsp;&nbsp;<span class="progressSpan">10%</span>&nbsp;&nbsp;
    速度:<span class="speed">20b/s</span><br><br>
    <button>點擊上傳</button>
    <button>取消上傳</button>
    <script>
    {
        let btns = document.querySelectorAll("button");
        let xhr = new XMLHttpRequest();
        btns[0].onclick = function(){
            let sTime = 0;//文件開始上傳時間
            let eTime = 0;//文件開始上傳時間
            let fileInitSize = 0;//文件開始上傳大小
            xhr.open("post","/fileUpload",true);
            //獲取上傳文件
            let file = document.querySelector(".myfile").files[0];
            let formData = new FormData();
            formData.append("imgFile",file);

            //upload事件監控文件上傳進度
            xhr.upload.onloadstart = function(){
                console.log("文件開始上傳");
                sTime = new Date().getTime();
                fileInitSize = 0;
            };
            xhr.upload.onprogress = function(evt){
                console.log("文件上傳中");
                //在onprogress事件中監控上傳進度
                let progress = (evt.loaded / evt.total * 100).toFixed(0);
                //將進度進行顯示
                document.querySelector("progress").value = progress;
                document.querySelector(".progressSpan").style.display = "inline-block";
                document.querySelector(".progressSpan").innerHTML = progress+"%";

                //監控文件上傳速度(需要onloadstart和onprogress的時間差,及時間差內文件已上傳的文件大小)
                eTime = new Date().getTime();
                //需要將時間差轉爲秒s
                let diffTime = (eTime-sTime)/1000;
                //各個進度文件上傳的文件大小
                let curFileSize = evt.loaded;
                let diffFileSize = curFileSize - fileInitSize;
                //獲取上傳速度(需要處理上傳的單位b/s kb/s)
                let speed = diffFileSize/diffTime;
                let unit = "";
                if(speed/1024>1){
                    speed = speed/1024;
                    unit = "b/s";
                }
                if(speed/1024>1){
                    speed = speed/1024;
                    unit = "kb/s";
                }
                document.querySelector(".speed").innerHTML = speed.toFixed(2)+unit;
                //使用當前文件大小/時間差 即文件上傳速度
                sTime = eTime;
                fileInitSize = curFileSize
            };
            xhr.upload.onabort = function(){
                console.log("取消文件上傳");
            };
            xhr.upload.onerror = function(){
                console.log("文件上傳失敗");
            };
            //上傳成功
            xhr.upload.onload = function(){
                console.log(xhr.responseText);
            };
            xhr.upload.onloadend = function(){
                console.log("文件上傳完成");
            };
            xhr.send(formData);
        };
        btns[1].onclick = function(){
            //取消文件上傳方法
            xhr.abort();
        };
    }
    </script>
</body>
</html>

後臺處理:

//監控文件上傳進度
router.post("/fileUpload",(ctx,next)=>{
    //通過前端append()中的name屬性即可獲取到對應數據或文件
    //ctx.request.files.imgFile.path即文件的臨時路徑,對臨時路徑中的文件轉存到服務器下路徑即可
    let fileData = fs.readFileSync(ctx.request.files.imgFile.path);
    
    //判斷文件夾不存在,需要先創建文件夾
    if(!fs.existsSync("static/imgs")){
        fs.mkdirSync("static/imgs/");
    }
    fs.writeFileSync("static/imgs/"+ctx.request.files.imgFile.name,fileData);
    
    ctx.body = {
        status:1,
        msg:"文件上傳成功"
    };
});

 結果:

8.回顧

1.ajax基本使用:創建XMLHttpRequest對象,xhr.open(),xhr.onload,xhr.send()

2.get/post在ajax中的使用

3.ajax中成功的返還:onload

4.返還數據格式:response,responseText,responseXML

5.FormData對象:創建FormData對象,form.append(name,value)

6.upload事件對象:各個監控文件上傳的事件方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章