NodeJs 第十七章 文件上传

前端上传文件有两种方式

  • 方式一:使用 FormData 发送 ajax 上传
// 创建FormData对象  
var formData = new FormData();    
  
// 添加文件数据  
var fileInput = document.querySelector('input[type="file"]');  
formData.append('file', fileInput.files[0]);  
  
// 创建XMLHttpRequest对象  
var xhr = new XMLHttpRequest();  
  
// 设置请求的配置信息  
xhr.open('POST', 'api/upload', true);  
xhr.send(formData);  
  
// 监听请求的完成事件  
xhr.onload = function() {  
  if (xhr.status === 200) {  
    console.log('上传成功!');  
  } else {  
    console.error('上传失败!');  
  }  
};
  • 方式二:使用 Form 表单上传(需要指定enctype="multipart/form-data")
<form action="/api/upload" method="POST" enctype="multipart/form-data">
  <p>
    <input type="text" name="a" />
  </p>
  <p>
    <input type="file" name="img" />
  </p>
  <p>
    <button>提交</button>
  </p>
</form>

文件上传的两种方式,他们有什么关系?

  1. FormData构造函数,用于创建FormData实例。
  2. 允许通过append()方法向FormData对象添加表单字段和文件数据。
  3. 自动将表单字段和文件数据封装成multipart/form-data格式的请求体,以便通过XMLHttpRequestFetch API发送。
  4. 在发送请求时,会自动将请求的Content-Type头部设置为multipart/form-data

multipart/form-data 是什么?

Multipart/form-data是一种在HTTP传输协议中用于在Web表单中传输文件的编码方式。它是一种灵活的编码方式,能够同时处理表单中的多个字段和文件上传。

multipart/form-data编码中,表单中的每个数据字段都被封装成一个消息体,并以一定的分隔符分隔消息体。这个编码方式使用了类似于multipart/mixed的格式,但主要用于表单数据的传输。每个消息体包含一个字段名称和字段值,以及一个可选的文件上传字段。

当使用multipart/form-data编码方式时,表单数据被封装成一条消息,每个控件对应消息的一部分。这种编码方式支持在提交表单时上传文件,因此被广泛应用于文件上传功能的实现中。

总结来说,multipart/form-data的编码方式是一种将表单中的每个数据字段封装成一个消息体的编码方式,并以一定的分隔符分隔消息体。这种编码方式支持在提交表单时上传文件,并能够处理多个字段和文件上传。

multipart/form-data 实现原理

在form-data编码中,表单数据被封装成一条消息,以标签为单元,用分隔符分开。这意味着表单数据被分割成多个部分,每个部分都以标签开头,以分隔符结尾。这样可以同时上传多个键值对或文件。

当上传文件时,除了普通的键值对数据外,每个文件都会被封装为一个独立的消息部分。每个文件部分都包含一个Content-Type头部,用于指定文件的媒体类型(即文件类型),以及一个Content-Disposition头部,用于描述该文件部分的名称和是否应该被显示或保存。

由于有分隔符隔离每个部分,所以form-data既可以上传键值对,也可以上传文件。同时,由于它采用了键值对的方式,所以可以上传多个文件或键值对。

POST / HTTP/1.1
Content-Length: 551
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="a"

1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="img"; filename="截屏2024-01-17 10.13.02.png"
Content-Type: <Content-Type header here>

(data)
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="b"

2
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="截屏2024-01-17 16.40.30.png"
Content-Type: <Content-Type header here>

(data)
------WebKitFormBoundary7MA4YWxkTrZu0gW--

上传文件为什么只能是 form-data 类型的请求体?

HTTP请求体主要有以下几种格式:

  • multipart/form-data:主要用于表单数据的提交,特别是文件上传。它会把表单数据封装成一条消息,以标签为单位,用分隔符分开。既可以上传键值对,也可以上传文件。
  • application/x-www-form-urlencoded:会将表单内的数据转换为键值对,用&分隔。当method为get时,会将表单数据编码为(name1=value1&name2=value2…),然后把这个字符串append到url后面,用?分隔。这个格式不能提交文件,区别于multipart/form-data。
  • raw:可以上传任意格式的文本,包括text、json、xml、html等。
  • binary:相当于Content-Type:application/octet-stream,只可以上传二进制数据,用来上传文件。由于没有键值,一次只能上传一个文件。

由此可以看出 application/x-www-form-urlencodedraw,只支持字符串,不支持二进制文件。binarymultipart/form-data都支持二进制文件,但 multipart/form-data可以以键值对的形式进行文件上传,试用起来更灵活。

node 服务如何获取到客户端上传的文件

我们可以借用 Express 官方维护的 multer 中间件

const express = require("express");
const multer = require("multer");
const path = require("path");
const app = express();
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, path.resolve(__dirname, "./public/upload"));
  },
  filename: function (req, file, cb) {
    // 时间戳-6位随机字符.文件后缀
    const timeStamp = Date.now();
    const ramdomStr = Math.random().toString(36).slice(-6);
    const ext = path.extname(file.originalname);
    const filename = `${timeStamp}-${ramdomStr}${ext}`;
    cb(null, filename);
  },
});

const upload = multer({
  storage,
  limits: {
    fileSize: 150 * 1024,
  },
  fileFilter(req, file, cb) {
    //验证文件后缀名
    const extname = path.extname(file.originalname);
    const whitelist = [".jpg", ".gif", "png"];
    if (whitelist.includes(extname)) {
      cb(null, true);
    } else {
      cb(new Error(`your ext name of ${extname} is not support`));
    }
  },
});

app.post("/api/upload", upload.single("img"), (req, res) => {
  const url = `/upload/${req.file.filename}`;
  res.send({
    code: 0,
    msg: "",
    data: url,
  });
});

const port = 5008;
app.listen(port, () => {
  console.log(`server listen on ${port}`);
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值