在发送 HTTP 请求的时候,最常用的请求方式就是 GET 和 POST ,这里简单写一下 Express 获取 GET 和 POST 数据的方式。

GET 数据

GET 数据发送的时候会写在 URL 地址中,格式就是 name=value ,比如 page=1 ,多条数据之间用 & 分隔。

Express 获取 GET 数据的方式也比较简单,在路由处理函数中使用 req.query.name 就可以获取,其中 name 就是查询参数名称:

app.get('/get', (req, res) => {
  const userName = req.query.userName;
  const password = req.query.password;
  res.json({userName, password});
});

在获取 radio 单选框时也是用 req.query.name 获取,一般一组单选框的 name 都是一样的,通过单选框的 name 可以获取 value

获取 checkbox 复选框也可以用 name 获取,一组复选框的 name 一般都不一样,复选框选中可以通过 name 获取 value 。复选框没有选中的情况下 namevalue 都不会出现在 URL 中,通过 res.query.name 获取的也是 undefined

POST 数据

获取 POST 数据需要使用 body-parser 中间件,下面使用 npm 安装 body-parser:

npm install body-parser --save

在路由处理函数中可以通过 req.body.name 获取 POST 数据:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

// 注册 body-parser 解析 URL 数据
app.use(bodyParser.urlencoded({extended: false}));
// 注册 body-parser 解析 JSON 数据
app.use(bodyParser.json());

app.post('/post', (req, res) => {
  const userName = req.body.userName;
  const password = req.body.password;
  res.send({userName, password});
});

我在注册 body-parser 中间件的时候调用了 urlencodedjson ,其中 urlencoded 就是普通的 URL Query 数据,格式为 name=value&name=valueheaderContent-type 一般为 application/x-www-form-urlencoded

json 就是解析 JSON 数据,JSON 数据的 headerContent-typeapplication/json ,body-parser 会根据 Content-type 来判断数据类型。

获取 JSON 数据可以直接使用 req.body

app.post('/post', (req, res) => {
  const jsonData = req.body;
  res.json(jsonData);
});

JSON 数据直接就可以使用,不需要再调用 JSON.parse 来转换。

使用 req.body 获取的 JSON 数据和客户端的格式是一样的,客户端发送的数据如果是 {"userName" : "Mr. Ma", "password": "123"}req.body 获取的数据也是 {"userName" : "Mr. Ma", "password": "123"}

上传文件

处理上传文件需要使用 multer 中间件,下面使用 npm 安装 multer :

npm install [email protected] --save

单文件

下面使用 multer 处理上传文件:

const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const app = express();

// 设置上传文件的存储位置
const upload = multer({dest: path.join(__dirname, 'upload')});

app.post('/upload', upload.single('imgFile'), (req, res) => {
  // 获取文件信息
  const fileInfo = req.file;
  // 把文件重命名为原来的文件名
  fs.renameSync(
    fileInfo.path,
    path.join(__dirname, 'upload', fileInfo.originalname)
  );
  res.send('上传完成');
});

我上面设置的文件存储位置是 upload 目录,上传的文件默认会随机生成一个文件名,在文件上传完成后可以给文件重命名。我这里为了方便,使用了 fs.renameSync 来同步修改文件名。

在处理上传单文件请求时,第二个参数需要调用 multer 上传配置的 singlesingle 的参数就是文件表单 name 。在路由处理函数中使用 req.file 可以获取文件信息,文件信息如下:

{
  "fieldname": "imgFile",
  "originalname": "test.gif",
  "encoding": "7bit",
  "mimetype": "image/gif",
  "destination": "D:\\web\\express-test\\upload",
  "filename": "d3ab37846b0179f51afc27ca32e79d76",
  "path": "D:\\web\\express-test\\upload\\d3ab37846b0179f51afc27ca32e79d76",
  "size": 109362
}

属性说明:

  • fieldname:文件表单名称
  • originalname:原来的文件名称
  • encoding:编码
  • mimetype:文件类型
  • destination:文件存储位置
  • filename:随机生成的文件名
  • path:文件存储位置,包含随机生成的文件名
  • size:文件大小(字节)

多文件

文件表单是可以多选的,下面处理多文件上传:

const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const app = express();

// 设置上传文件的存储位置
const upload = multer({dest: path.join(__dirname, 'upload')});

app.post('/upload', upload.array('imgFile'), (req, res) => {
  const fileInfo = req.files;
  // 把上传的文件重命名为原来的名称
  fileInfo.forEach(file => {
    fs.renameSync(
      file.path,
      path.join(__dirname, 'upload', file.originalname)
    );
  });
  res.send(fileInfo.length + '个文件上传完成');
});

处理多文件上传使用的是 multerarray ,参数还是表单 name ,在路由处理函数中可以使用 req.files 获取文件信息。

多文件的文件信息和单文件是一样的,只是多文件是一个数组。

多个不同 name 的文件字段

form 可以包含多个不同 name 的文件表单,FormData 也可以添加多个不同 name 的键值对。

下面处理一组不同 name 的文件上传:

// 设置上传文件的存储位置
const upload = multer({dest: path.join(__dirname, 'upload')});

// fields 配置
const fieldsConfig = [
  {name: 'imgFile', maxCount: 1},
  {name: 'textFile', maxCount: 1}
];

app.post('/upload', upload.fields(fieldsConfig), (req, res) => {
  const fileInfo = req.files;
  // 把 imgFile 字段的文件重命名为原来的名称
  fileInfo.imgFile.forEach(file => {
    fs.renameSync(
      file.path,
      path.join(__dirname, 'upload', file.originalname)
    );
  });
  // 把 textFile 字段的文件重命名为原来的名称
  fileInfo.textFile.forEach(file => {
    fs.renameSync(
      file.path,
      path.join(__dirname, 'upload', file.originalname)
    );
  });
  res.send('文件上传完成');
});

使用 multerfields 可以处理多个文件字段的文件上传,fields 的参数是一组 fields 配置,fields 配置的格式如下:

[
  {name: 'imgFile', maxCount: 1},
  {name: 'textFile', maxCount: 1}
]

name 就是文件字段名称,maxCount 就是文件数量限制,超出限制的文件会无法上传。

在路由处理函数中可以使用 req.files.name 来获取指定字段的文件,获取的也是一个对象数组:

[
  {
    "fieldname": "textFile",
    "originalname": "新建文本文档.txt",
    "encoding": "7bit",
    "mimetype": "text/plain",
    "destination": "D:\\web\\express-test\\upload",
    "filename": "cd0c8055d865a61dc582204a999bf210",
    "path": "D:\\web\\express-test\\upload\\cd0c8055d865a61dc582204a999bf210",
    "size": 5
  }
]

multer 配置

在初始化 multer 的时候可以传入一个对象来配置文件存储,下面是一个简单的配置:

const multer = require('multer');

const upload = multer({
  // 存储位置
  dest: path.join(__dirname, 'upload'),
  limits: {
    // 最大文件大小为 1MB
    fileSize: 1024 * 1024,
    // 最大字段数,默认不限制
    fields: 2,
    // 可以同时上传的最大文件数,默认不限制
    files: 1
  }
});

下面是 limits 可以设置的上传限制:

  • fieldNameSize:设置上传字段名的最大长度,默认为 100 字节
  • fieldSize:设置上传字段值的最大长度,默认为 Infinity
  • fields:设置可以同时上传的最大字段数,默认为 Infinity
  • fileSize:设置上传文件的最大大小(以字节为单位),默认为 Infinity
  • files:设置可以同时上传的最大文件数,默认为 Infinity
  • parts:设置上传字段和文件的最大总数,默认为 Infinity
  • headerPairs:设置请求头中键值对的最大数量,默认为 2000

multer 默认会随机生成文件名,你也可以通过设置 multer 存储引擎的方式来设置文件名:

const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();

// multer 存储引擎配置
const storage = multer.diskStorage({
  // 设置文件存储位置
  destination: (req, file, cb) => {
    cb(null, path.join(__dirname, 'upload'));
  },
  // 设置文件名
  filename: (req, file, cb) => {
    // 获取当前的时间戳
    const timestamp = new Date().getTime();
    // 把文件名设置为时间戳 + 原来的文件名
    cb(null, timestamp + '-' + file.originalname);
  }
});

// 初始化 multer
const upload = multer({ storage: storage });

上面把文件名设置为 时间戳-原文件名 主要是为了避免出现同名的文件。