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 访问该图片。 项目仓库

相关推荐
KYGALYX4 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法4 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax