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

相关推荐
欣然~15 分钟前
法律案例 PDF 批量转 TXT 工具代码
linux·前端·python
一个小废渣17 分钟前
Flutter Web端网络请求跨域错误解决方法
前端·flutter
符文师1 小时前
css3 新特性
前端·css3
小王不爱笑1321 小时前
SpringBoot 配置文件
java·spring boot·后端
ct9781 小时前
WebGL开发
前端·gis·webgl
想用offer打牌2 小时前
Spring AI vs Spring AI Alibaba
java·人工智能·后端·spring·系统架构
C_心欲无痕2 小时前
前端页面渲染方式:CSR、SSR、SSG
前端
果粒蹬i2 小时前
生成式 AI 质量控制:幻觉抑制与 RLHF 对齐技术详解
前端·人工智能·easyui
码农幻想梦3 小时前
实验五 spring入门及IOC实验
java·后端·spring
WordPress学习笔记3 小时前
解决Bootstrap下拉菜单一级链接无法点击的问题
前端·bootstrap·html