本文将使用 NestJs 与 Multer 搭建一个文件上传服务,并逐步添加文件本地保存、文件大小限制、文件类型限制功能。
准备工作
-
使用 nest new 命令新建一个名为'upload-example'的项目,并指定使用pnpm包管理器。
bashnest new upload-example -p pnpm
-
进入项目根目录,安装 Multer 的类型声明包。
bashpnpm i -D @types/multer
-
在项目根目录使用 nest generate 命令自动生成一个 upload 模块,选择 REST API 风格即可,下一步输入 y 就行。
bashnest g resource upload
简易实现
-
在 VsCode 中打开
/src/upload
下的upload.controller.ts
文件,删除多余代码,只留下一个空类即可,如下: -
从
@nestjs/common
中引入Post
、UseInterceptors
、UploadedFile
模块,从@nestjs/platform-express
中引入FileInterceptor
模块。typescriptimport { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common'; import { UploadService } from './upload.service'; import { FileInterceptor } from '@nestjs/platform-express';
-
在 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
文件代码如下:
-
-
使用
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 访问该图片。 项目仓库