NestJs文件上传由浅入深

本文将使用 NestJs 与 Multer 搭建一个文件上传服务,并逐步添加文件本地保存、文件大小限制、文件类型限制功能。

准备工作

  1. 使用 nest new 命令新建一个名为'upload-example'的项目,并指定使用pnpm包管理器。

    bash 复制代码
    nest new upload-example -p pnpm
  2. 进入项目根目录,安装 Multer 的类型声明包。

    bash 复制代码
    pnpm i -D @types/multer
  3. 在项目根目录使用 nest generate 命令自动生成一个 upload 模块,选择 REST API 风格即可,下一步输入 y 就行。

    bash 复制代码
    nest g resource upload

简易实现

  1. 在 VsCode 中打开/src/upload下的upload.controller.ts文件,删除多余代码,只留下一个空类即可,如下:

  2. @nestjs/common中引入PostUseInterceptorsUploadedFile模块,从@nestjs/platform-express中引入FileInterceptor模块。

    typescript 复制代码
    import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';
    import { UploadService } from './upload.service';
    import { FileInterceptor } from '@nestjs/platform-express';
  3. 在 UploadController 中添加 simple 方法,代码如下:

    typescript 复制代码
    @Controller('upload')
    export class UploadController {
      constructor(private readonly uploadService: UploadService) {}
    
      @Post()
      @UseInterceptors(FileInterceptor('file'))
      simple(@UploadedFile() file: Express.Multer.File) {
        console.log(file);
        return this.uploadService.create(file);
      }
    }
    • @Post()代表 post 方法。

    • @UseInterceptors(FileInterceptor('file'))代表使用FileInterceptor处理上传的form data里的 file 字段的数据,也可以不指定字段名,直接处理整个表单数据。

    • @UploadedFile()使用该装饰器从 request 中取出 file。

    • uploadService文件代码如下:

  4. 使用pnpm run start命令启动项目,然后使用任一类postman工具发送一个上传请求即可。本文使用的是apifox,如下图:

    注意:端口号换成你自己的,根目录下的 main.js 中可以查看,nest应用一般默认3000。请求方法类型选 post,参数放 body 中,格式选 form-data,参数名为 file(FileInterceptor('file')中设置的名字,如没有设置,则可以不写参数名)。

至此,我们就实现了一个简单的上传接口。我们可以看见发起请求后,控制台打印出了文件信息,但没有保存文件,接下来我们就来实现文件的保存等功能。

文件保存

FileInterceptor装饰器传入第二个参数{ dest: 'uploads/' },表示将文件保存到根目录下的 uploads 文件夹中,如果没有则会自动创建该文件夹。
使用apifox发送请求后(先重启服务),我们会发现根目录下的 uploads 文件夹中多了一个二进制文件,而我上传的是一个 png 图片。这是因为我们没有设置文件后缀名,下面我们来解决这个问题。

首先我们先从 multer 中引入 diskStorage 模块,然后再在 FileInterceptor 中配置相关参数,如下:

ts 复制代码
import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFile
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer'; // 新增代码

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post()
  @UseInterceptors(
    FileInterceptor('file', {
      // 新增代码
      storage: diskStorage({
        destination: 'uploads/', // 设置文件保存位置为根目录下的 uploads 文件夹
        filename: (_req, file, cb) => {
          // 生成一个随机字符串
          const randomName = Array(32)
            .fill(null)
            .map(() => Math.round(Math.random() * 16).toString(16))
            .join('');
          // 设置文件名
          return cb(null, `${randomName}${file.originalname}`);
        }
      })
    })
  )
  simple(@UploadedFile() file: Express.Multer.File) {
    return this.uploadService.create(file);
  }
}

现在我们重启服务后,再发送上传图片请求,就可以在 uploads 文件夹下看见刚刚通过接口上传的图片文件了。

限制文件类型、大小

在 src 下新建一个 config/multerConfig.ts 文件,并添加以下代码:

ts 复制代码
import { extname } from 'path';
import { diskStorage } from 'multer';

export const multerConfig = {
  limits: {
    fileSize: 1024 * 1024 * 5
  },
  fileFilter: (_req, file: Express.Multer.File, cb) => {
    // 限制上传图片类型文件
    if (file.mimetype.match(/(jpg|jpeg|png|gif)$/)) {
      return cb(null, true);
    }

    return cb(null, false);
  },
  storage: diskStorage({
    destination: 'uploads/',
    filename: (_req, file, cb) => {
      const randomName = Array(32)
        .fill(null)
        .map(() => Math.round(Math.random() * 16).toString(16))
        .join('');
      // 获取文件后缀
      const suffix = extname(file.originalname);
      return cb(null, `${randomName}${suffix}`);
    }
  })
};

在 upload.controller.ts 文件引入该配置对象,即添加以下代码到该文件
import { multerConfig } from 'src/config/multerConfig';

然后修改 upload.controller.ts 文件中的 simple 方法的 FileInterceptor 配置,如下:

ts 复制代码
@Post()
@UseInterceptors(FileInterceptor('file', multerConfig))
simple(@UploadedFile() file: Express.Multer.File) {
  return this.uploadService.create(file);
}

最后在 upload.service.ts 文件中添加上传文件不符合的要求的响应信息,如下:

ts 复制代码
import { Injectable } from '@nestjs/common';

@Injectable()
export class UploadService {
  create(file: Express.Multer.File) {
    if (!file) {
      return {
        code: 400,
        message: 'Only image files are allowed!'
      };
    }

    return {
      code: 200,
      data: {
        url: `http://localhost:3000/static/${file.filename}`
      },
      message: 'success'
    };
  }
}

重启服务,分别上传图片与其他类型的文件,观察接口返回的响应数据。到这里我们就实现了文件类型与大小的限制,更多配置可以查看 multer 官方文档

支持访问已上传文件

前面我们实现了文件的上传,但我们必须要进入 nest 项目里才能查看,而不能通过 url 访问已上传的资源。接下来我们就来实现通过 url 访问已上传的资源。

其实我们只需要搭建一个静态资源服务,在main.js中添加几行代码即可实现,如下:

ts 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express'; // 新增代码,引入模块

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  // 新增代码,设置静态资源路径与访问前缀
  app.useStaticAssets('uploads', {
    prefix: '/static/'
  });
  await app.listen(3000);
}
bootstrap();

现在我们上传一个图片后,即可通过接口返回的 url 访问该图片。 项目仓库

相关推荐
蟾宫曲2 小时前
在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)
前端·npm·vue3·vite·element-plus·计时器
秋雨凉人心2 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
qq13267029403 小时前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
海绵波波1073 小时前
flask后端开发(10):问答平台项目结构搭建
后端·python·flask
网络风云4 小时前
【魅力golang】之-反射
开发语言·后端·golang
魏时烟4 小时前
css文字折行以及双端对齐实现方式
前端·css
Q_19284999064 小时前
基于Spring Boot的电影售票系统
java·spring boot·后端
2401_882726485 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203985 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github