zoukankan      html  css  js  c++  java
  • 发送formData数据为什么不用设置 content-type?

    表单在上传文件数据时,会设置 enctype="multipart/form-data",那么请求报文会设置 "Content-Type: multipart/form-data",但是我们在发送 Ajax 请求时,就不会去设置他,报文会自动生成一个 content-type 。

    那我手动去设置 content-type 会发生什么呢?

    首先,我写一个 ajax 请求,

    <form id="form">
             <input type="file" name="files" multiple>
            <label for="username">用户名:</label>
            <input type="text" name="username" id="username">
            <label for="password">密码:</label>
            <input type="password" name="password" id="password">
            <input type="button" value="提交" id="btn">
    </form>
    
    <script>
        var form = document.getElementById('form');
        var btn = document.getElementById('btn');
        btn.onclick = function () {
            var formData = new FormData(form);
            var xhr = new XMLHttpRequest();
    
            xhr.open('post', 'http://localhost:8080/Ajax');
            //xhr.setRequestHeader('Content-Type', 'multipart/form-data');
    
            xhr.send(formData);
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    console.log(xhr.responseText);
                }
            }
        }
    </script>
    

    然后,我们在服务端接收和处理请求,这里我用 formidable 来解析 form-data 数据

    const http = require('http');
    const path = require('path');
    const formidable = require('formidable');
    
    const server = http.createServer();
    server.on('request', async (req, res) => {
        if (req.url == '/Ajax') {
            if (req.method == 'GET') {
                let pathName = path.join(__dirname, 'ajax.html');
                let data = await readFile(pathName, 'utf8');
                res.end(data);
            }
            if (req.method == 'POST') {
                const form = formidable({multiples:true});
                form.parse(req, (err, fields, files) => {
                    if(err){
                        console.log(err);
                        res.statusCode = 500;
                        res.end('解析错误');
                    }else{
                        res.writeHead(200, { 'content-type': 'application/json' });
                        res.end(JSON.stringify({ fields, files }, null, 2));
                    }
                  });
                return;
            }
        } else {
            res.end('not found');
        }
    });
    
    server.listen(8080);
    console.log('服务器启动成功');
    

    请求成功后,服务端返回一个 json 字符串,结构是这样的:

    {
      "fields": {
        "username": "",
        "password": ""
      },
      "files": {
        "files": {
          "size": 33341, 
          "path": "C:\Users\luzhi\AppData\Local\Temp\upload_81c6907665bd2dbbf2acf30a5eacb5c0",
          "name": "The_Half_of_It_poster.jpeg",
          "type": "image/jpeg",
          "mtime": "2020-06-14T10:56:58.473Z"
        }
      }
    }
    

    这个 path 是文件存储的临时路径,文件是以二进制的数据存储。再来看一下请求头和请求体的内容,

    可以看到, content-type 中有一个boundary ,文件是以二进制编码格式传递。这个 boundary 是什么呢?我们再 view source 看一下,

    可以看见,这个 boundary 就是不同数据的一个标记字符串,formidable 解析数据时也是以这个来分块解析的。

    如果我们手动设置 content-type 会怎样呢?

    xhr.setRequestHeader('Content-Type', 'multipart/form-data');
    

    content-type 没有了 boundary,错误信息:

    Error: bad content-type header, no multipart boundary

    formidable 无法解析。但请求体依然会有这种特殊的分界的编码字符串,

    由此可知,这个 boundary 是浏览器自己生成的带有特定格式的随机字符串,用以分隔不同的数据体,并在content-type 中标明,供服务端接收解析数据。

    知道了这个,那我们自己去解析 "multipart/form-data" 编码格式的请求体,实现一个类似于 formidable 功能的函数。


    解析 form-data 数据,实现 formidable 函数

    关于 formParse 的代码 https://github.com/Arduka/ajax-formParser

  • 相关阅读:
    项目发展规划 题解
    善意的投票&小M的作物 题解
    方格取数加强版 题解
    BZOJ1001 狼抓兔子 题解
    a
    一个搬运
    代码“小白”的温故而知新(一)-----OA管理系统
    工作流-----WorkFlow
    温习SQL语句
    浅谈MVC基础
  • 原文地址:https://www.cnblogs.com/arduka/p/13127003.html
Copyright © 2011-2022 走看看