前端文件上传(后端node.js)

116 阅读1分钟

1、文件上传

1.1 后端代码实现

const http = require('http');
const querystring = require("querystring");
const fs = require('fs');
const server = http.createServer((req, res) => {
  // 处理跨域请求
  res.setHeader('Access-Control-Allow-Origin', '*');
  if (req.url === '/upload') {
    if (req.method.toLowerCase() === 'get') {
      // 返回一个用于文件上传的form
      res.writeHead(200, { 'content-type': 'text/html' });
      res.end(
        `
        <form action="upload" enctype="multipart/form-data" method="POST">
          <input type="file" name="upload" multiple="multiple" />
          <input type="submit" value="Upload" />
  </form>
        `
      )
    } else if (req.method.toLowerCase() === 'post') {
      if (req.headers['content-type'].indexOf('multipart/form-data') !== -1) {
        parseFormData(req, res);
      } else {
        res.end('你好')
      }
    }
  }
});

function parseFormData (req, res) {
  req.setEncoding('binary');
  let body = '';  // 文件数据
  let fileName = ''; // 文件名

  // 边界字符串
  const boundary = req.headers['content-type']
                      .split('; ')[1]
                      .replace('boundary=', '');
  req.on('data', function (chunk) {
    body += chunk;
  });

  req.on("end", function() {
    const file = querystring.parse(body, "\r\n", ":");
    const fileInfo = file["Content-Disposition"].split(";");
    for (value in fileInfo) {
      if (fileInfo[value].indexOf("filename=") !== -1) {
        fileName = fileInfo[value].substring(11, fileInfo[value].length - 1);
        // 这是处理什么情况的?
        if (fileName.indexOf("\\") != -1) {
          fileName = fileName.substring(fileName.lastIndexOf("\\") + 1);
        }
        break
      }
    }

    const entireData = body.toString();
    contentType = file["Content-Type"].substring(1);
    //获取文件二进制数据开始位置,即contentType的结尾
    const upperBoundary = entireData.indexOf(contentType) + contentType.length;
    const shorterData = entireData.substring(upperBoundary);
    // 替换开始位置的空格
    const binaryDataAlmost = shorterData
        .replace(/^\s\s*/, "")
        .replace(/\s\s*$/, "");
    // 去除数据末尾的额外数据,即: "--"+ boundary + "--"
    const binaryData = binaryDataAlmost.substring(0, binaryDataAlmost.indexOf("--" + boundary + "--"));
    const bufferData = new Buffer.from(binaryData, "binary");

    fs.writeFile(fileName, bufferData, function(err) {
      if (err) {
        console.log(err);
      }
      res.end("sucess");
    });
});
}

server.listen(3001);

这里处理formData数据时没有使用中间件进行处理,所以有点麻烦(中间件处理方案后续补上)。 参考链接:原生nodejs 处理文件上传

1.2 前端代码实现

1.2.1 <form>表单提交

代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
  <form
    action="http://localhost:3001/upload"
    method="POST"
    enctype="multipart/form-data"
  >
    <input type="file" name="file" id="">
    <input type="submit" value="提交">
  </form>
</body>
</html>
form有关提交表单的属性

补充一下,好久不写,都不记得了

  • action:处理表单提交的URL。可被<button><input type="submit"><input type="image">元素上的formaction属性覆盖
  • enctype:当method属性值为post时,enctype就是将表单内容提交给服务器的MIME类型;可选:application/x-www-form-urlencoded(默认),multipart/form-data(表单包含type=fileinput元素时使用),text/plain(出现于HTML5,用于调试,可被<button><input type="submit">或 <input type="image">元素上的 formenctype属性覆盖)
  • method:可选值:postgetdialog(如果表单在 <dialog>元素中,提交时关闭对话框。此值可以被 <button><input type="image">元素中的formmethod属性覆盖。)
  • novalidate:表示提交表单时不需要验证表单;该属性可以被表单中的 <button><input type="submit">或 <input type="image">元素中的 formnovalidate属性覆盖。
  • target:表示提交表单后,在哪里显示响应信息

参考链接:form

注意:form表单提交不存在跨域问题

1.2.2 原生XmlHttpRequest上传文件

代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
  <input type="file" id="upload">
  <button class="upload-btn">点击上传</button>
  <script>
    const btn = document.querySelector('.upload-btn');
    btn.addEventListener('click', function (e) {
      let xhr;
      if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
      } else {
        // 兼容IE
        xhr = ActiveXObject('Microsoft.XMLHTTP');
      }
      let fd = new FormData();
      const file = document.querySelector('#upload');
      console.log(file.files);
      fd.append('file', file.files[0]);
      // 初始化
      console.log('UNSEDN', xhr.readyState);
      // 打开连接
      xhr.open('post', 'http://localhost:3001/upload');
      // xhr.setRequestHeader('Content-Type', 'multipart/form-data');
      xhr.send(fd);
      xhr.onreadystatechange = function () {
        if(this.readyState === 2){
       // 接收到响应头
        console.log("HEADERS_RECEIVED",xhr.readyState);
        }else if(this.readyState === 3){
            // 响应体加载中
          console.log("LOADING",xhr.readyState);
        }else if(this.readyState === 4){
            // 加载完成
            console.log("DONE",xhr.readyState);
        }
      }
    });
  </script>
</body>
</html>

1.2.3 jQuery实现

代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
  <input type="file" id="upload">
  <input class="upload-btn" type="button" value="点击上传" />
  <script>
    $('.upload-btn').click(function (e) {
      const fd = new FormData();
      console.log($('#upload')[0].files[0]);
      const file = $('#upload')[0].files[0]
      fd.append('file', file);
      $.ajax({
        url: 'http://localhost:3001/upload',
        type: 'post',
        data: fd,
        contentType: false,
        processData: false,
        success: function (e) {
          console.log(e);
        }
      })
    })
  </script>
</body>
</html>

1.2.4 axios 实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script></head>
<body>
  <input type="file" id="upload">
  <input class="upload-btn" type="button" value="点击上传" />
  <script>
    const btn = document.querySelector('.upload-btn');
    btn.addEventListener('click', function () {
      const file = document.querySelector('#upload').files[0];
      const fd = new FormData();
      fd.append('file', file);
      axios.post('http://localhost:3001/upload', fd).then(res => {
        console.log(res);
      }).catch(e => {
        console.error(e);
      });
      return false;
    });
  </script>
</body>
</html>