掘金 后端 ( ) • 2024-05-02 14:00

前言

这篇文章是作者个人学习NestJS的产出,参考了小满ZS的Nest教程

后端实现

为了实现上传静态资源图片的功能,一共要经历四步:

  1. 安装中间件multer
  2. 配置Multer
  3. 创建上传接口
  4. 配置静态资源目录

安装中间件multer

Multer是Node.js的中间件,用于处理multipart/form-data类型的数据,主要用于上传文件,包括视频、图片等。

其中,multipart/form-data是一种HTTP请求的编码类型,适合传输包含文件的表单数据,允许将表单信息分为多个部分,每个部分都可以有自己的内容类型。这使得其可以同时发送文本字段和文件数据,非常适合文件上传的场景。

multer可以通过以下指令安装

npm i multer

为了获取ts的类型推断,安装multer的ts包,这个包只需在生产环境用到,因此加上-D

npm i @types/multer -D

配置Multer

Multer有四个基本设置:

  • diskStorage:控制文件的存储方式,存储到磁盘里,可定义文件的储存路径和文件名
  • memoryStorage:控制文件的储存方式,储存到内存中,适用于临时文件处理
  • limits:定义上传文件的大小限制
  • fileFilter:根据文件类型,过滤文件

下面给出Multer在NestJS里的配置

import { Module } from '@nestjs/common';
import { UplodeService } from './uplode.service';
import { UplodeController } from './uplode.controller';
//导入MulterModule,用来在NestJS里注册模块
import { MulterModule } from '@nestjs/platform-express';
//导入multer库的diskStorage,用来配置文件存储方式
import { diskStorage } from 'multer';
//导入path模块的extname和join函数,用来处理文件路径和文件名
//extname()用来获取文件的扩展名,如.jpg、.png等
//join()用来将两个路径合并
import { extname, join } from 'path';

@Module({
  //在Module的imports数组里,注册并配置Multer模块
  //调用MulterModule的register()方法进行配置,传入配置对象
  imports: [MulterModule.register({
    //配置项的storage属性决定文件的储存位置和方式
    //通过multer模块的diskStorage来进行配置,表示文件存入磁盘,传入配置对象
    storage: diskStorage({
      //destination配置文件上传后的路径
      //__dirname是全局变量,表示当前文件目录。假设index.js文件在/home/ubuntu/index.js这里,那么
      //__dirname表示:/home/ubuntu
      //path模块的join()函数,用来合并两个目录。
      //拿上面的例子距离,合并后的路径为:/home/images
      //因为../images表示上一级目录的images文件夹
      destination: join(__dirname, "../images"),
      //定义文件名,这里通过函数来决定文件的名字
      //这个函数传入三个参数:request、file对象和callback函数
      //callback函数接收两个参数:错误对象和文件名。如果没有错误通常传入null
      //callback函数用来通知multer,文件命名已经完成了,如果没有错误的话可以进行下一步操作了
      filename: (_, file, callback) => {
        //这里的文件名为字符串,具体是:当前时间的时间戳 + 文件扩展名
        const fileName = `${new Date().getTime() + extname(file.originalname)}`
        return callback(null, fileName)
      }
    })
  })],
  controllers: [UplodeController],
  providers: [UplodeService],
})
export class UplodeModule { }

创建上传接口

现在我们配置好了multer,将上传的文件存储到磁盘里,并且指定了存放上传文件的文件目录,规定了上传文件的文件名。

现在我们需要实现一个上传接口,我们将上传功能单独看作是一个模块,因此我们先通过脚手架创建新的模板

nest g source upload

选择RESTful风格

然后我们在upload.controller中开始编写上传接口

import { Controller, Post } from '@nestjs/common';
import { UplodeService } from './uplode.service';

//导入UseInterceptor装饰器和UploadedFile装饰器
//UseInterceptor用于在Controller中应用拦截器,而UploadedFile用于获取上传的文件
import { UseInterceptors, UploadedFile } from '@nestjs/common';
//导入FileInterceptor,是一个处理文件上传的拦截器
import { FileInterceptor } from '@nestjs/platform-express';

//定义路径为/upload
@Controller('upload')
export class UplodeController {
  constructor(private readonly uplodeService: UplodeService) { }
  //指定接口为Post方法,路径为/upload/pic
  @Post("pic")
  //使用@UseInterceptors()装饰器来应用FileInterceptor拦截器,拦截名字为file的上传文件
  @UseInterceptors(FileInterceptor("file"))
  //通过@UploadedFile()装饰器,来从request请求中提取出上传的文件
  upload (@UploadedFile() file){
    //对文件的处理逻辑
    console.log(file)
    return true
  }
}

看到这里,你是否和我有一样的疑惑: 怎么突然就有了file对象?这个file对象是个啥?

别着急!下面我们一起深挖这段multer处理文件的逻辑

首先可以确定的是,在console.log(file)这一段,我们以及获取到了文件,并且可以操作文件了。这个时候用户上传的文件,已经被保存到了目标文件夹中。所以将文件保存到服务器的过程,一定发生在获取到file对象之前。

所以,将上传的文件保存到服务器中,是装饰器完成的。更具体地说是使用@UseInterceptors()装饰器来应用FileInterceptor拦截器的时候。

当使用FileInterceptor拦截器时,会首先处理文件上传流程,然后将文件保存到指定目录,然后按照配置重命名文件。在这些工作完成后,拦截器再将文件对象传递给upload(@UploaderFile() file)方法中,其中的file参数已经是处理后的文件对象了。

配置静态目录

既然是上传静态资源,我们可以配置静态目录,通过添加前缀,让用户可以通过统一的入口去获取到静态资源

image.png

image.png

静态目录的配置在main.ts中实现

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
//导入session
import * as session from "express-session";

//由于multer是express的插件,为了可以在NestJS中使用,需要导入NesrExpressApplication,这时express平台的应用接口
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from "path"

async function bootstrap() {
    const app = await NestFactory.create<NestExpressApplication>(AppModule);
    app.enableVersioning({
      type: VersioningType.URI
    })
    //注册使用
    app.use(session({
      secret: "Icicle_Crino",
      name: "Crino",
    }))
    //生成静态目录,通过useStaticAssets()来定义静态目录
    //上文讲到过,join()是path模块的方法,用于合并两个路径
    //join(__dirname, "images") __dirname是全局变量,表示当前文件所在目录
    //join()最后返回的是:当前路径下的/images文件夹
    app.useStaticAssets(join(__dirname, "images"), {
      //前缀名,意味着images文件夹下的资源,可以通过/Crino前缀访问,如
      //http://localhost:3000/Crino/1714551064615.jpg
      prefix: "/Crino"
    })
    await app.listen(3000);
  }
bootstrap();

总结

到这里我们就完成了通过NestJS实现静态资源上传的全过程。一共分为四步:

  1. 安装中间件multer
  2. 配置Multer
  3. 创建上传接口
  4. 配置静态资源目录

作者也是刚刚开始学习NestJS新手,如果文章中存在错误或笔误的地方,欢迎和谐讨论交流~ 如果大家想看的话,作者后续会更新前端教程,教你在前端如何实现文件上传功能

最后祝大家五一快乐