《Node.js 文件上传神器:Multer ✨》

547 阅读6分钟

在 Node.js 开发中,文件上传是一个常见的需求。而 multer 是一个非常强大且易用的中间件,专门用于处理 multipart/form-data 类型的表单数据,尤其是文件上传。

一、Multer 的安装

使用 npm 可以轻松安装 multer

npm install multer

二、Multer 的功能

  1. 文件上传处理
  • 能够自动解析客户端发送的包含文件上传的 multipart/form-data 请求,将文件数据和其他表单字段分离出来。
  • 提供多种存储方式,可以将上传的文件存储在本地文件系统、内存或云存储等位置。
  • 允许自定义上传文件的命名规则和存储路径,方便管理和组织上传的文件。
  1. 文件过滤
  • 可以基于文件类型过滤,只接受特定类型的文件上传,例如只允许上传图片文件或文档文件。通过检查文件的 MIME 类型或文件扩展名来进行过滤。
  • 可以基于文件大小过滤,设置上传文件的大小限制,防止用户上传过大的文件导致服务器资源耗尽。
  1. 错误处理
  • 能够检测文件上传过程中可能出现的各种错误,如文件类型不匹配、文件大小超过限制、上传过程中出现网络问题等。
  • 允许开发者自定义错误响应,向客户端返回特定的错误消息和状态码。
  1. 多文件上传支持
  • 支持同时处理多个文件的上传,用户可以在一个表单中选择多个文件进行上传。
  • 可以处理表单中以数组形式出现的文件字段。

三、Multer 的用法

3.1Multer 启动

1.创建存储配置:

const multer = require('multer');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    // 指定文件存储的目标目录
    cb(null, './uploads');
  },
  filename: function (req, file, cb) {
    // 自定义文件名
    cb(null, Date.now() + '-' + file.originalname);
  },
});

这里创建了一个存储配置对象,指定了文件存储的目标目录和文件名的生成规则。

2.创建 multer 实例:

const upload = multer({ storage });

使用存储配置创建一个 multer 实例。

3.在路由中使用:

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

app.post('/upload', upload.single('file'), (req, res) => {
  // 处理上传后的文件
  res.send('File uploaded successfully!');
});

在路由中使用 upload.single('file') 中间件来处理单个文件上传。这里 'file' 是表单中文件字段的名称。

3.2Multer 功能

1.文件上传处理

  • 创建存储配置,指定文件存储在本地文件系统的目标目录和文件名生成规则。
const multer = require('multer');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    // 指定文件存储的目标目录为 './uploads'
    cb(null, './uploads');
  },
  filename: function (req, file, cb) {
    // 以当前时间戳和原始文件名组合生成文件名
    cb(null, Date.now() + '-' + file.originalname);
  },
});

const upload = multer({ storage });
  • 在路由中使用中间件处理单个文件上传。
const express = require('express');
const app = express();

app.post('/uploadSingle', upload.single('file'), (req, res) => {
  res.send('Single file uploaded successfully!');
});

当客户端向 /uploadSingle 路由发送包含单个文件的请求时,multer 会将文件存储在指定的目录中,并根据配置生成文件名。

2.文件过滤:

  • 基于文件类型过滤,只接受特定的 MIME 类型文件。
const fileFilter = function (req, file, cb) {
  if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
    cb(null, true);
  } else {
    cb(new Error('Only JPEG and PNG images are allowed.'), false);
  }
};

const storageWithFilter = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './uploads');
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  },
});

const uploadWithFilter = multer({ storage: storageWithFilter, fileFilter });

app.post('/uploadFiltered', uploadWithFilter.single('file'), (req, res) => {
  res.send('Filtered file uploaded successfully!');
});

在这个例子中,只有 JPEG 和 PNG 类型的图片文件会被接受,其他类型的文件会触发错误。

  • 基于文件大小过滤,设置最大文件大小为 5MB。
const maxSize = 5 * 1024 * 1024; // 5MB

const fileFilterBySize = function (req, file, cb) {
  if (file.size <= maxSize) {
    cb(null, true);
  } else {
    cb(new Error('File size exceeds the limit.'), false);
  }
};

const storageWithSizeFilter = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './uploads');
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  },
});

const uploadWithSizeFilter = multer({
  storage: storageWithSizeFilter,
  fileFilter: fileFilterBySize,
});

app.post('/uploadSizeFiltered', uploadWithSizeFilter.single('file'), (req, res) => {
  res.send('Size-filtered file uploaded successfully!');
});

如果上传的文件大小超过 5MB,会触发错误响应。

3.错误处理

  • 捕获文件上传过程中的错误,并返回自定义的错误响应。
app.post('/uploadWithErrorHandling', uploadWithSizeFilter.single('file'), (req, res, next) => {
  if (req.file && req.file.error) {
    // 如果有文件上传错误,返回错误响应
    res.status(400).send({ error: req.file.error.message });
  } else {
    res.send('File uploaded successfully or no file was uploaded.');
  }
});

在这个例子中,如果文件上传过程中出现错误,会检查 req.file.error 是否存在,如果存在,则返回包含错误消息的 400 错误响应。

4.多文件上传支持

  • 处理多个文件上传,以数组形式存储。
const multipleUpload = multer({ storage: storageWithFilter }).array('files', 5); // 最多接受 5 个文件

app.post('/uploadMultiple', multipleUpload, (req, res) => {
  res.send('Multiple files uploaded successfully!');
});

客户端可以在表单中使用多个文件字段,名称为 files,最多上传 5 个文件。

  • 处理多个文件上传,每个文件有不同的字段名。
const fieldsUpload = multer({ storage: storageWithFilter }).fields([
  { name: 'image1', maxCount: 1 },
  { name: 'image2', maxCount: 1 },
]);

app.post('/uploadFields', fieldsUpload, (req, res) => {
  res.send('Files with different field names uploaded successfully!');
});

这个例子中,客户端可以上传两个文件,分别使用 image1image2 作为字段名,每个字段最多上传一个文件。

5.处理小文件且需要快速响应

  • 当处理一些非常小的文件(例如图标、小尺寸的图片等)时,将文件存储在内存中可以避免磁盘 I/O 操作,从而加快处理速度。
  • 例如,在一个实时性要求较高的应用中,用户上传一个小的头像图片,希望能够尽快看到更新后的头像。使用内存存储可以快速处理这个小文件,然后立即返回响应给用户,而不需要等待磁盘写入操作完成。
const multer = require('multer');
const express = require('express');

const storage = multer.memoryStorage();
const upload = multer({ storage });

const app = express();

app.post('/upload', upload.single('file'), (req, res) => {
  // 在这里可以对存储在内存中的文件进行快速处理
  res.send('File uploaded and processed in memory!');
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

四、Multer 的 API 详解

  1. multer.diskStorage(options)
  • destination:回调函数,用于指定文件存储的目标目录。接收 req(请求对象)、file(正在处理的文件对象)和 cb(回调函数)作为参数。
  • filename:回调函数,用于指定上传文件的文件名。同样接收 reqfilecb 作为参数。
  1. multer.memoryStorage():将文件存储在内存中,适用于小文件或需要快速处理的情况。
  2. multer({ storage }):使用存储配置创建一个 multer 实例。
  3. upload.single(fieldname):处理单个文件上传。fieldname 是表单中文件字段的名称。
  4. upload.array(fieldname, maxCount):处理多个文件上传,以数组形式存储。fieldname 是表单中文件字段的名称,maxCount 是可选的最大文件数量限制。
  5. upload.fields(fields):处理多个文件上传,每个文件有不同的字段名。fields 是一个对象数组,每个对象包含 name(字段名)和 maxCount(可选的最大文件数量限制)。

总之,multer 中间件为 Node.js 中的文件上传提供了强大而灵活的解决方案,通过简单的配置和 API 调用,可以轻松实现文件上传功能,并满足各种不同的需求。