1. 技术栈前端/后端 原生JS  test

2.代码部分

  2.1 前端

    2.1.1 Form表单提交方式  文件名: upload.form.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>upload by form</title>
  </head>
  <body>
    <form action="/upload" enctype="multipart/form-data" method="post">
      <input type="file" name="avatar" id="avatar" multiple="multiple" />
      <input type="submit" value="form上传头像" id="submit" />
    </form>
    <img id="img" />
  </body>
  <script>
    window.onload = () => {
      const avatar = document.getElementById("avatar");
      const img = document.getElementById("img");
      avatar.onchange = e => {
        const file = e.target.files[0];
        //创建读取文件的对象
        const reader = new FileReader();
        //使用该对象读取file文件
        reader.readAsDataURL(file);
        //读取文件成功后执行的方法函数
        reader.onload = function(e) {
          //读取成功后返回的一个参数e,整个的一个进度事件
          //选择所要显示图片的img,要赋值给img的src就是e中target下result里面
          //的base64编码格式的地址
          img.src = e.target.result;
        };
      };
    };
  </script>
</html>

    2.1.2  Ajax异步提交方式 文件名: upload.ajax.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>upload by Ajax</title>
  </head>
  <body>
    <input type="file" id="avatar" multiple="multiple" />
    <button type="button" id="submit">ajax上传头像</button>
    <img src="" id="img" />
  </body>
  <script>
    window.onload = () => {
      const btn = document.getElementById("submit");
      const img = document.getElementById("img");
      const avatar = document.getElementById("avatar");
      avatar.onchange = e => {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = e => {
          img.src = e.target.result;
        };
      };
      submit.onclick = () => {
        if(!avatar.files[0]) {
          alert('选择图片!')
          return false
        }
        const formData = new FormData();
        formData.append("avatar", avatar.files[0]);
        const xhr = new XMLHttpRequest();
        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText)
          }
        };
        // 上传进度
        xhr.upload.addEventListener(
          "progress",
          e => {
            if (e.lengthComputable) {
              console.log("percent: ", (e.loaded / e.total) * 100 + "%");
            }
          },
          false
        );
        // 上传路径这里写成相对路径 避免环境变化
        xhr.open("POST", "./upload");
        xhr.send(formData);
      };
    }; 
  </script>
</html>

  2.2 后端部分   文件名: upload.2.js

const http = require("http");
const fs = require("fs");
const formidable = require("formidable");
const pathFn = require("path");
http
  .createServer((req, res) => {
    if (req.url === "/upload" && req.method === "POST") {
      const form = new formidable.IncomingForm();
      const savePath = pathFn.join(__dirname, "/upload");
      // 检查文件加是否已经存在 这里用同步方法
      if (!fs.existsSync(savePath)) {
        fs.mkdirSync(savePath);
      }
      form.uploadDir = savePath;
      form.parse(req, (err, fields, files) => {
        res.writeHead(200, { "Content-Type": "text/plain" });
        // 取文件路径 和 文件名字
        const { path, name } = files.avatar;
        // 重命名
        fs.rename(path, pathFn.join(savePath, "/", name), err => {
          if (err) {
            res.writeHead(200, { "Content-Type": "text/plain" });
            res.end(err);
          }
          res.writeHead(200, { "Content-Type": "application/json" });
          res.end(JSON.stringify({code: 200, data: '/upload/' + name}));
        });
      });
    } else if (req.url === "/user-ajax") {
      const html = fs.readFileSync("./upload.ajax.html");
      res.writeHead(200, { "Content-Type": "text/html; charset=UTF8" });
      res.end(html);
    } else if (req.url === "/user-form") {
      const html = fs.readFileSync("./upload.form.html");
      res.writeHead(200, { "Content-Type": "text/html; charset=UTF8" });
      res.end(html);
    } else if (req.url === "/upload-ok") {
      const html = fs.readFileSync("./upload.ok.html");
      res.writeHead(200, { "Content-Type": "text/html; charset=UTF8" });
      res.end(html);
    } else {
      res.writeHead(200, { "Content-Type": "text/html; charset=UTF8" });
      res.end("No found!");
    }
  })
  .listen(7777);

3. 两种提交方式的不同

  表单提交成功后, 直接跳转到了 /upload 返回 { code: 200, data: '/upload/XXXXX.png' }

  ajax异步提交成功后, 是返回 json 数据, 页面不刷新.

  这里的处理方案, 如果是用form表单提交, 可以修改后端返回直接重定向到一个提交成功页面(upload.ok.html

          // ajax提交 成功之后返回 json
          // res.writeHead(200, { "Content-Type": "application/json" });
          // res.end(JSON.stringify({code: 200, data: '/upload/' + name}));
          
          // form表单 成功之后 重定向页面
          res.writeHead(302, { 'Location': './upload-ok'})

4. 补充说明

  这里博主 试着抓取  ajax 与 form表单 两种提交方式的 req 对象对比,  在 本机 localhost 的环境下 是有一个  Sec-fetch-mode 的值的别去 form 是 navigate , ajax 是 cors , 但是走网络请求后 是没有 Sec-fetch-mode ,实在找不到两者如何来区分;

  记得Express.js 框架提供一个 req.xhr 属性,如果请求由 Ajax 发起将会返回 true , 后期研究之后再补充;

 

5. 资料来源指向 .

FormData 对象的使用

FileReader