浏览器上传图片到服务器时通过input type="file" 以及onchange事件的配合实现的,element和vant的ui库的图片上传都是基于这个

input的files属性

1.众所周知,一级dom事件不同于addeventlisner这种,它的function中的this指向当前事件对象

具体来说 addEventListener与onclick这种的区别就是this

不过好像也不需要this来指出dom,因为e身上就有target和srcElement,这个就是当前触发的dom。。。。

let b = document.getElementById('b')
    let a = document.getElementById('a')
    b.addEventListener('click', e => {
        console.log(e, this);//事件对象  window
    })
    a.onclick = function (e) {
        console.log(e, this);//事件对象, 当前dom
    }

回来正题,然后获取input元素后会发现,input元素身上比其他元素多一个files属性,这个是用来放选择上传的文件的,文件列表

可以通过打印console.dir(this)来看元素详情,

如果给input元素添加multiple属性,那么就可以多选上传文件,不过如果第一次选了两个文件,第二次又选了一个文件,每一次的选择都会覆盖前面的

所以这个多选指的是一次可以选择多个上传

方式1,file

file方式主要是通过FormData()格式化数据,不设置请求头,
 这样浏览器就会根据传入的data来判断contentType,formdata应该是二进制的
  所以可以看到传输的方式是binary二进制的

前端

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="file" id="inputa" multiple>
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
  <script>
    // file方式主要是通过FormData()格式化数据
    // multipart/form-data; boundary=
    let inputa = document.getElementById("inputa")
    inputa.onchange = function () {
      // this是input元素 this.files是选择的文件列表
      let fd = new FormData()
      console.log(this.files)
      // files文件列表无法通过普通数组等循环遍历,需要通过迭代器循环遍历
      for (let file of this.files) {
        console.log('object', file)
        fd.append(file.name, file)
      }
      // 循环把文件列表的文件添加到formdata中

      console.log(fd)

      $.ajax({
        url: "http://localhost/upload",//请求路径
        type: "POST",
        data: fd,
        dataType: 'JSON',
        // contentType: "application/x-www-form-urlencoded",
        contentType: false,//不设置请求头,
        // 这样浏览器就会根据传入的data来判断contentType,
        // 所以可以看到传输的方式是binary二进制的
        processData: false,//同上同理
        success(data) {
          if (200 == data.code) {
            console.log("上传成功!");
            console.log(data)
          } else {
            console.log("上传失败!");
            console.log(data)
          }
        },
        error() {
          console.log("与服务器通信发生错误");
        }
      })

    }
  </script>
</body>

</html>

 

后端

const multiparty = require("multiparty");

router.post('/upload', function (req, res) {
    // 1.根据传过来的base64,以及要改成的名字,动态存入图片
    console.log('req', req.body, req.request);
    let form = new multiparty.Form({ uploadDir: './public/img' })
    form.parse(req, function (err, filed, files) {
        console.log(filed, files)
        // let dataBuffer = new Buffer(filed, 'filed');
        // console.log(dataBuffer)
    })
    res.send({ img: 'imgsrc' })
})

这里有两个点

1.如果contentType如图不设置,那么后端就需要通过multiparty来接收和存储文件,因为后端的req.body中不会有值,空的

图示

file_contexts 和genfs_contexts的作用_上传

可以看到req.body为空

2.而如果设置成application/x-www-form-urlencoded,那么后端的req.body中会有相应的二进制值

file_contexts 和genfs_contexts的作用_上传_02

可以看到req.body为一堆乱码二进制