Logo
Section 1 / 4

上传文件

让我们实现应用程序业务逻辑中最核心的功能。

导入

上传文件

让我们实现应用程序业务逻辑中最核心的功能。

我们的应用程序的业务逻辑主要依赖于文件上传,因为我们正在构建一个可以视为我们自己私有图书馆的应用程序。文件上传使我们能够尽可能地扩展应用程序的功能,利用这些功能来操作和组织我们的文件。

先决条件

我们将使用 Multer,这是一个广为人知的 npm 包,作为上传文件的中间件。为了配置文件上传到服务器,我们需要安装以下内容:

  • multer 负责处理上传到服务器的文件。
  • body-parser 负责解析请求体。

让我们在 index.js 中配置 body-parser。

1// backend/index.js 2 3// ... 其余代码 4const bodyParser = require("body-parser"); 5 6// ... 其余代码 7 8app.use(bodyParser.urlencoded({ extended: true })); 9app.use(bodyParser.json()); 10 11// ... 其余代码

静态资源

我们不会将实际文件存储在数据库中,因为这会降低查询性能。相反,我们将文件存储在服务器磁盘或任何其他磁盘存储中,而数据库仅存储指向这些文件的引用或指针。

这个指针或引用信息可以是任何有助于唯一标识已存储文件的内容。文件名是必须保存的关键信息之一。之后,我们可以根据所开发的应用程序需求,添加其他相关信息。

以下是存储上传文件的方法:

  1. 创建一个用于存储上传文件的新目录,命名为 backend/uploads,如下所示:
    1mkdir uploads
  2. 配置 Express 使用 uploads 目录来存储静态数据,即我们这里的上传文件。
1// backend/index.js 2 3// ... 其余代码 4 5app.use('/uploads', express.static('./uploads')); 6 7// ... 其余代码

模型目录

现在我们知道如何存储上传的文件了,那么需要的信息又该存放在哪里?我们将使用数据库为每个文件存储一条文档记录。这样不仅便于检索存储的信息,还能保存关于文件内容的一些元数据。

  1. 我们在 models 目录中创建一个新的模式文件 file.js:
    1cd models 2touch file.js
  2. 我们创建文件的模式:
1// backend/models/file.js 2 3const mongoose = require("mongoose"); 4const Joi = require("joi"); 5 6const fileSchema = new mongoose.Schema({ 7 createdAt: { 8 type: Date, 9 default: Date.now, 10 }, 11 createdBy: { 12 type: mongoose.Schema.Types.ObjectId, 13 ref: "user", 14 required: true, 15 }, 16 filePath: { 17 type: String, 18 required: true, 19 }, 20 name: { 21 type: String, 22 required: [true, "上传的文件必须有名称"], 23 }, 24 description: { 25 type: String, 26 required: [true, "上传的文件必须有描述"], 27 }, 28}); 29 30const File = mongoose.model("file", fileSchema); 31 32const validate = (file) => { 33 const schema = Joi.object({ 34 filePath: Joi.string().required(), 35 name: Joi.string().required(), 36 description: Joi.string().required(), 37 }); 38 return schema.validate(file); 39}; 40 41module.exports = { File, validate };

每个文件必须包含以下内容:

  • name 和 description 标记为 required,并带有错误提示信息。
  • createdBy 是指向用户对象的外键。
  • filePath 用于快速从磁盘位置获取文件。
  • createdAt 如果未指定时间,则自动设置为当前时间。

Multer 配置

下一步是将 Multer 配置到应用中。我们可以将 Multer 的配置视为一种中间件,它在将请求传递给处理器之前提取上传的文件。

  1. 我们创建一个文件 middleware/multer.js:
    1cd middleware 2touch multer.js
  2. 我们可以这样实现 Multer 的配置:
1// backend/middleware/multer.js 2 3const multer = require("multer"); 4 5const storage = multer.diskStorage({ 6 destination: (req, file, cb) => { 7 cb(null, "uploads"); 8 }, 9 filename: (req, file, cb) => { 10 const ext = file.mimetype.split("/")[1]; 11 cb(null, `item-${file.fieldname}-${Date.now()}.${ext}`); 12 }, 13}); 14 15exports.upload = multer({ storage });
  • destination 指定上传文件的基础存储目录。
  • filename 指定 Multer 如何命名上传的文件。
  • mimetype 用于确定文件扩展名,以安全地重命名文件。
  • Date.now() 确保文件名始终唯一。

文件控制器

  1. 我们创建文件的控制器:
    1cd controllers 2touch file.js
  2. 然后定义上传功能:
1// backend/controllers/file.js 2 3const { File, validate } = require("../models/file"); 4const { upload } = require("../middleware/multer"); 5 6const BASE_URL = "http://localhost:3000"; 7 8exports.upload = async (req, res) => { 9 try { 10 const { error } = validate(req.body); 11 if (error) return res.status(400).send(error.details[0].message); 12 13 const file = new File({ 14 name: req.body.name, 15 description: req.body.description, 16 createdBy: req.user._id, 17 filePath: `${BASE_URL}/uploads/${req.file.filename}`, 18 createdAt: Date.now(), 19 }); 20 21 await file.save(); 22 res.status(201).send({ message: "文件上传成功" }); 23 } catch (err) { 24 res.status(500).send("服务器内部错误"); 25 } 26};
  • BASE_URL 定义为服务器上所有上传项链接的基础地址。
  • name 和 description 在验证后从请求体中提取。
  • createdBy 是上传文件的认证创建者。
  • createdAt 表示文件上传到服务器的时间。
  • filePath 包含该文件在服务器上的公开 URL,以便轻松访问。

路由上传功能

我们可以这样定义上传功能的路由:

1// backend/routes/index.js 2 3// ...其余导入 4 5const auth = require("../middleware/auth"); 6const { upload } = require("../middleware/multer"); 7 8// ...其余代码 9 10// 将 'upload' 请求映射到上传函数 11router.post("/upload", auth, upload.single("file"), fileController.upload); 12 13module.exports = router;
  • 我们应用 auth 中间件,确保当前用户已成功认证。
  • 我们使用 upload 中间件,让 Multer 处理上传的文件,并将其传递给控制器。
  • 我们将请求传递给 fileController.upload 文件,由其处理数据写入数据库的过程。

我们可以在下方终端中运行上述任意命令:

Terminal

测试上传功能

现在,我们可以使用 Postman 测试上传功能。

No configuration found for ID: f606f797d4218b3e5369b8206cfa3cf2a412d9fd95a184a4d76acaa3640b3b6c

我们可以使用 Postman 测试上传功能,步骤如下:

[]

如果成功,我们将收到一条提示:“文件上传成功”。

注意: 我们在请求体中使用 form-data 类型来上传文件。前端也将采用相同方式。

开发客户端

处理文本文件