开发背景
任何一个成熟的网站都少不了文件上传,我的个人博客也不例外,为了方便修改头像、上传文章封面,所以来开发这么一个文件上传的接口。
开始开发
安装依赖
这里我们需要设置静态资源目录,让NestJS知道我们要上传到哪个目录下。
npm i @nestjs/serve-static @nestjs/platform-express
依赖配置
在我们的modules中进行静态资源目录的配置,这里我放在app.module.ts中。
// ...
import { ServeStaticModule } from "@nestjs/serve-static";
import { join } from "path";
@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, "../", "public"),
}),
],
//...
})
export class ApiModule {}
首先引入静态资源模型和path.join。
在Module注解中imports引入静态资源模型,并进行配置,使用join方法组合路径设置静态资源的根目录(rootPath)为src/public目录下。
Controller实现
首先定义Controller骨架,我这里目前封装有一个基类BaseController,所以UploadController就继承这个BaseController,并且先引入UploadService,稍后再来定义。
import { Controller } from "@nestjs/common";
import { BaseControlle } from "../core/base.controller";
import { UploadService } from "../service/upload.service";
@Controller("upload")
export class UploadController extends BaseControlle {
constructor(private readonly uploadService: UploadService) {
super();
}
}
类中定义文件上传的方法,因为该接口是post请求,所以先引入Post注解,在接口方法uploadLocal内将操作抛给service去处理。
import { Controller, Post } from "@nestjs/common";
import { BaseControlle } from "../core/base.controller";
import { UploadService } from "../service/upload.service";
@Post("local")
uploadLocal() {
return this.uploadService.uploadLocal();
}
抛给service时一定要将请求参数一同抛过去,里面含有上传的文件信息,需要引入UploadedFiles注解拿到参数request。
import {
// ...
UseInterceptors
} from "@nestjs/common";
// ...
uploadLocal(@UploadedFiles() request) {
return this.uploadService.uploadLocal(request);
}
此时如果我们想设置上传的文件数量、修改上传name、添加额外的参数时,需要引入nestjs的拦截器UseInterceptors和文件拦截器FileFieldsInterceptor,并进行设置。
import {
// ...
UseInterceptors
} from "@nestjs/common";
// ...
@UseInterceptors(FileFieldsInterceptor([
{ name: "file", maxCount: 1 },
{ name: "custom", }
]))
uploadLocal(@UploadedFiles() request) {
return this.uploadService.uploadLocal(request);
}
可以通过修改name和maxCount来修改上传name和设置上传的文件数量。
添加额外参数只需要在FileFieldsInterceptor方法的数组参数中扩充即可。
这样我们上传文件的Controller就已经实现了。
Service实现
在实现Controller时,我们事先定义了UploadService,现在就来实现这个Service。Service的骨架我就不写了,这里主要是来实现uploadService.uploadLocal方法。
import * as fs from "fs";
//...
async uploadLocal(request) {
const file = request.file[0];
try {
fs.readdirSync(`./public/uploads`);
} catch (err) {
fs.mkdirSync(`./public/uploads`);
}
const now = this.Moment().format("YYYYMMDD");
try {
fs.readdirSync(`./public/uploads/${now}`);
} catch (err) {
fs.mkdirSync(`./public/uploads/${now}`);
}
fs.writeFileSync(
`./public/uploads/${now}/${file.originalname}`,
file.buffer,
);
// 数据返回
return {
code:0,
data:{
fileUrl: `uploads/${now}/${file.originalname}`,
}
}
}
接下来介绍一下上述代码实现了什么?
- 引入
fs模块用来读写文件 - 因为我这里只上传一个文件,所以读取
request.file[0]拿到第一个文件 - 尝试读取
public下是否有uploads目录,如果没有则创建 - 定义当日的时间并格式化为
YYYYMMDD - 尝试读取
public/uploads下是否有当日的目录,如果没有则创建 - 将文件写入到当日的目录中
- 将本地路径返回
最终存放的路径如:public/uploads/20230416/demo.png
到此为止,一个简易的文件上传就实现了。
结语
我是一名前端程序员,但不止于前端,如果文章哪里写的有些欠妥,欢迎评论指正,大家一起学习,一起进步~