Appearance
文件上传
环境配置
上传文件是网站开发中的常用操作,使用NestJs进行上传非常简单,本章我们来掌握这块知识。
我们会使用到 multer 扩展包实现上传,所以需要安装扩展包与类型支持。
pnpm add multer
pnpm add -D @types/multer
创建模块与控制器
nest g mo common
//flat参数指不创建子目录结构
nest g co common/upload --no-spec --flat
模块定义
上传配置可以放在控制器方法,也可以放在模块中,下面是在模块中定义。
import { Global, Module } from '@nestjs/common'
import { MulterModule } from '@nestjs/platform-express'
import { diskStorage } from 'multer'
import { extname } from 'path'
import { UploadController } from './upload.controller'
@Global()
@Module({
imports: [
MulterModule.registerAsync({
useFactory() {
return {
storage: diskStorage({
//文件储存位置
destination: 'uploads',
//文件名定制
filename: (req, file, callback) => {
const path = Date.now() + '-' + Math.round(Math.random() * 1e10) + extname(file.originalname)
callback(null, path)
},
}),
}
},
}),
],
controllers: [UploadController],
})
export class CommonModule {}
基本使用
现在我们在控制器中体验文件上传
上传处理
下面在控制器使用
import { Controller, Post, UploadedFile, UseGuards, UseInterceptors } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import { FileInterceptor } from '@nestjs/platform-express'
@Controller('upload')
export class UploadController {
@Post('image')
@UseInterceptors(FileInterceptor('file'))
image(@UploadedFile() file: Express.Multer.File) {
return file
}
}
下面使用postman等测试工作来测试上传是否成功,如果成功会返回以下结果。
- 下面数据包含在data中是因为使用拦截器处理,请查看拦截器章节了解
{
"fieldname": "file",
"originalname": "hj-small.png",
"encoding": "7bit",
"mimetype": "image/png",
"destination": "uploads",
"filename": "1658807155785-7305320730.png",
"path": "uploads/1658807155785-7305320730.png",
"size": 40442
}
类型校验
下面示例只允许上传图片文件
export class UploadController {
@Post('image')
@UseInterceptors(
FileInterceptor('file', {
fileFilter(req: any, file: Express.Multer.File, callback: (error: Error | null, acceptFile: boolean) => void) {
if (file.mimetype.includes('image')) {
callback(new UnsupportedMediaTypeException('文件类型错误'), false)
} else {
callback(null, true)
}
},
}),
)
...
}
优化代码
如果上传分图片/文档等不同类型的上传处理,使用上面的方式需要在控制器的不同方法定义重复度很高的代码 。下面我们来简化这个过程。
装饰器聚合
我们将 @UseInterceptors(FileInterceptor('file')) 通过装饰器聚合来简化代码,下面在 ./upload.ts 文件中定义。
import { applyDecorators, UseInterceptors } from '@nestjs/common'
import { FileInterceptor } from '@nestjs/platform-express'
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface'
export function upload(fieldName = 'file', options: MulterOptions = {}) {
return applyDecorators(UseInterceptors(FileInterceptor(fieldName, options)))
}
现在在控制器中就可以简化操作了
import { Controller, Post, UploadedFile } from '@nestjs/common'
import { UploadDecorator } from './decorator/upload.decorator'
@Controller('upload')
export class UploadController {
@Post()
@upload('file', {
fileFilter(req: any, file: Express.Multer.File, callback: (error: Error | null, acceptFile: boolean) => void) {
if (file.mimetype.includes('image')) {
callback(new UnsupportedMediaTypeException('文件类型错误'), false)
} else {
callback(null, true)
}
})
upload(@UploadedFile() file: Express.Multer.File) {
return file
}
}
抽离验证功能
下面对上传的文件类型验证进行抽离,修改 ./upload.ts 定义 fileMimetypeFilter 函数用于对上传类型进行验证。
export function fileMimetypeFilter(...mimes: string[]) {
return (req: any, file: Express.Multer.File, callback: (error: Error | null, acceptFile: boolean) => void) => {
if (mimes.some((mime) => file.mimetype.includes(mime))) {
callback(null, true)
} else {
callback(new UnsupportedMediaTypeException('文件上传类型错误'), false)
}
}
}
现在可以在控制器中对上传文件进行验证了
import { Controller, Post, UploadedFile } from '@nestjs/common'
import { UploadDecorator,fileMimetypeFilter} from './upload'
@Controller('upload')
export class UploadController {
@Post()
@UploadDecorator('file', {fileFilter: fileMimetypeFilter('image')})
upload(@UploadedFile() file: Express.Multer.File) {
return file
}
}
最终封装
经过上面的学习,现在应该对nest.js上传已经掌握了,下面我们进行整合,使用上传使用变得更方便 。
功能整合
下面我们修改 ./upload.ts 对图片上传与文档上传定义
import { applyDecorators, UnsupportedMediaTypeException, UseInterceptors } from '@nestjs/common'
import { FileInterceptor } from '@nestjs/platform-express'
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface'
//上传类型验证
export function filterFilter(type: string) {
return (req: any, file: Express.Multer.File, callback: (error: Error | null, acceptFile: boolean) => void) => {
if (!file.mimetype.includes(type)) {
callback(new UnsupportedMediaTypeException('文件类型错误'), false)
} else {
callback(null, true)
}
}
}
//文件上传
export function Upload(field = 'file', options: MulterOptions) {
return applyDecorators(UseInterceptors(FileInterceptor(field, options)))
}
//图片上传
export function Image(field = 'file') {
return Upload(field, {
//上传文件大小限制
limits: Math.pow(1024, 2) * 2,
fileFilter: filterFilter('image'),
} as MulterOptions)
}
//文档上传
export function Document(field = 'file') {
return Upload(field, {
//上传文件大小限制
limits: Math.pow(1024, 2) * 5,
fileFilter: filterFilter('document'),
} as MulterOptions)
}
实际使用
现在可以在控制器中方便的使用上传了
import { Controller, Post, UploadedFile } from '@nestjs/common'
import { image } from './upload'
@Controller('upload')
export class UploadController {
@Post('image')
@Image()
image(@UploadedFile() file: Express.Multer.File) {
return file
}
}
文件访问
图片虽然上传成功了,但还不能通过域名访问。使用NestJs配置非常简单,在man.ts主文件中定义
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { NestExpressApplication } from '@nestjs/platform-express'
import { join } from 'path'
async function bootstrap() {
//传递类型NestExpressApplication
const app = await NestFactory.create<NestExpressApplication>(AppModule)
...
//静态资源访问
app.useStaticAssets('uploads', {prefix: '/uploads'})
await app.listen(3000)
}
bootstrap()
现在可以通过url正常访问图片了,如 http://localhost:3000/uploads/xxxx.jpeg