A front and rear side interaction (ajax FormData objects and upload files)

1. Key Points grasp and knowledge

## master key

  1. Understand basic use ajax
  2. We will use the XMLHttpRequest object implements data exchange
  3. Learn onreadystatechange server response information (status code)
  4. Objects will be used to upload files FormData
  5. Learn upload event object (the event object under XMLHttpRequest)

## knowledge points

  1. using ajax
  2. XMLHttpRequest object
  3. FormData objects
  4. upload event object

2. Log brief review

1, proposed ajax verify the user name needs;

2, after validation errors, if resolved by jumping a lot of trouble;
 

3. Use ajax to solve the problem of verifying the user name

- ajax is: Ajax namely "Asynchronous Javascript And XML" (Asynchronous JavaScript and XML)

- ajax basic use:

  • New XMLHttpRequest object;
 let xhr = new XMLHttpRequest();
  • Parameter configuration request
 xhr.open("get","/checkUser",true); //true是异步,false是同步
  • Receiving a return value
  xhr.onload = function(){
     let res = JSON.parse(xhr.responseText);
  }
  • Sending a server request
xhr.send();

Case is achieved by asynchronous ajax no refresh verification Username:

 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", (cxt, next) => {
    let username = userData.find(item=>item.username === cxt.query.username);
    console.log(username);
    if(username){
        cxt.body = {
            status:1,
            msg:"用户名正确"
        };
    }else{
        cxt.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对象中获得的
      let res = JSON.parse(xhr.responseText);
      // console.log(JSON.parse(xhr.responseText).msg);
      // console.log(xhr.response);
      exchange.style.display = "block";
      exchange.innerHTML = JSON.parse(xhr.responseText).msg;
      console.log(res.status);
      
      if(res.status === 1){
        //获取到的数据是JSON格式的,需要将其转为对象
        exchange.style.color = "green";
      }else{
        exchange.style.color = "red";
      }
    }
    xhr.send();
  }
}
  </script>
</body>

4. For a detailed explanation of ajax

4.1get Precautions

  - get parameter passing by parmas

  - get and querystring problems through mass participation url

get parameter passing in two ways:

  1. queryString, mass participation by queryString have length restrictions, the default is 2048
  2. /get/id

Note the way parameter passing point:

  • querystring parameter passing and get / post request mode is the two concepts, instead of using the parameter passing is querystring get request, POST parameters can also be transmitted through querystring;
  • When querystring parameter passing by way of background reception address is not affected; but, if using the get / id transmission mode / reference background must / get /: id mode reception parameters, and the parameter value is obtained by ctx.params;

queryString parameter passing mode: the above example

'/ Request address / parameter' parameter passing mode: using '/ Address Request /: the parameter' receives the address, parameter values ​​acquired using 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请求
router.get("getInfo","/getInfo/:id",ctx=>{
    console.log(ctx.params);//{ id: '1' }
    ctx.body = {
        status:1,
        msg:"请求成功"
    };
});

4.2post Precautions

post generally not mass participation by queryString, because queryString mass participation limited length (servers limit), the default 2048.

post reference parameter passing is passed through the HTTP body, the body must be set to pass parameter encoding format . Form with the default encoding format form <form action = "" enctype = "application / x-www-form-urlencoded"> </ form>. form form may be omitted, but can not be omitted when ajax request.

important point:

  1. When transmitting data to set http header text format;
  2. Obtain header information: getAllResponseHeaders or getResponseHeader;

- Send text data when you need to set http header format:

xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");  //默认编码
xhr.setRequestHeader("Content-type","multipart/form-data");  //二进制编码,上传文件时使用
xhr.setRequestHeader("Content-type","application/json");  //json编码:传输数据也需要时JSON格式

Example: Note that the use of post request, the background koa-body module must be used in order to obtain the parameters

<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>
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:"请求成功"
    };
});

When the http header text formatted as JSON: json data transmission format is also required, you must use the JSON.stringify () format the data into json

 //正文请求头设置为json时,传输数据也需要是JSON格式
                xhr.setRequestHeader("content-type","application/json; charset=utf-8");
                let data = JSON.stringify({
                    username:'zs',
                    age:12
                });

  - Get the header information: getAllResponseHeaders () Gets all header information or getResponseHeader ( "request header attributes") that it takes a header information

                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
                }

Results: All header information

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

4.3 synchronous and asynchronous ajax

  • Set Asynchronous requests or other requests not affect the implementation of the code;
  • When setting synchronous execution, like other requests or code that may be executed after the request, will be performed

Asynchronous: Asynchronous setting request does not affect the execution of code or other requests

<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>

Result: The button to print a request does not affect the results of two buttons

 

Asynchronous: When setting synchronous execution, like other requests or code that may be executed after the request, will be performed

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

 result:

 

5.onreadystatechange

5.1 onreadystatechange

onreadystatechange: there function processing server response whenever readyState changes, the onreadystatechange function will be executed.

5.2 readyState

readyState: state server response information there.

  • 0: The request is not initialized (proxy is created, but not yet call open () method)
  • 1: server connection is established ( `open` method has been called)
  • 2: request has been received ( `send` method has been called, and the state and the head is already available)
  • 3: process the request (download, `responseText` property already contains partial data)
  • 4: request has been completed, and the response is ready (download operation has been completed)

5.3 status common status codes

Common status Status Code Status --http
HTTP status code description

100

carry on. The remaining portion continues to respond, submit a request

200

success

301

Permanent move. Requesting resources permanently to a new location

302

Temporary move. Zero resource request to a new location

304

Unmodified. Than the last request for the resource is not modified, the response does not contain the resource content

401

Unauthorized requires authentication

403

Prohibited. Request is denied

404

Not found, the server does not need to find resources

500

Internal Server Error. The server encountered an error and can not fulfill the request

503

Server is unavailable. Temporary service overloaded and can not process the request

Example: 

<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>

 In fact, there are using onload server response information and returns a status code: onload will be more concise recommended

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

5.4 return data type  

  • Server returned json data: xhr.responseText to get
xhr.responseText  //来获取
  • Server xml data returned:
  1. xhr.responseXML // get the value
  2. Response server provided in the content-type content ctx.set ( "content-type", "text / html");
  3. Rewriting distal XML (case back Type content-type is not specified): xhr.overrideMimeType ( 'text / xml; charset = utf-8'); Note that the property can be no space between the
xhr.responseXML //获取值

 Example:

Front-end 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>

Backstage:

//获取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>`;
});

 result:

To prevent the background did not write the XML content-type, need to rewrite XML format in the front:

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

6. Use FormData to Upload Files

6.1 Creating objects FormData

important point:

  1. <Input type = "file" class = "myfile"> acquired by the array is the class attributes files;
  2. Create a file upload objects through new FormData ();
  3.  formData.append (name, value); name name must be consistent with the background when receiving, value may be a normal data file
  4. File upload must be transmitted by way of the text, it is necessary to use the post request; content-type will automatically set the FormData use, you do not need to be set manually
  5. Upon receiving the background data, name attribute () can be obtained in the corresponding data or append files distal
  6. Then dump files fs corresponding to the module. That temporary path ctx.request.files.img.path file, dump down the path to the server file to a temporary path
  7. It is possible folder permissions problems, you need to manually open the dump file permissions. And the folder does not exist, create a folder

Example:

front end:

<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对象,进行上传
            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);
            };
            xhr.send(formData);
        };
    </script>
</body>

Backstage:

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");

 The results: { "status": 1, "msg": "File uploaded successfully"}

6.2 monitor the upload progress --upload event

The following events are under the upload events:

  • onloadstart start upload
  • onprogress data transfer in (evt.total: total size to be transmitted; evt.loaded: uploading the current file size;)
  • onabort upload operation to terminate (cancel upload xhr.abort ())
  • onerror upload failed
  • onload uploaded successfully
  • onloadend upload is complete (whether successful or not)

  File upload progress monitoring examples:

important point:

  • Monitor the file upload speed (requires onloadstart and onprogress time difference and the time difference file uploaded file size), the current file size / time difference, document upload speed;
  • Processing units need to upload b / s kb / s;

File upload page:

<!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>

Background Processing:

//监控文件上传进度
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:"文件上传成功"
    };
});

 result:

7. Recalls

1.ajax basic use: create XMLHttpRequest object, xhr.open (), xhr.onload, xhr.send ()

2.get / post in use in ajax

3.ajax the successful return: onload

4. The data format returned: response, responseText, responseXML

5.FormData objects: Create FormData objects, form.append (name, value)

Event monitoring method for each file upload: 6.upload event object

Published 95 original articles · won praise 115 · views 120 000 +

Guess you like

Origin blog.csdn.net/qq_34569497/article/details/100917080