前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nest.js 实战 (五):如何实现文件本地上传

Nest.js 实战 (五):如何实现文件本地上传

原创
作者头像
白雾茫茫丶
修改2024-08-01 14:17:09
630
修改2024-08-01 14:17:09
举报
文章被收录于专栏:Nest.js 实战系列

前言

最近在开发用户管理模块,需要上传用户头像,正好顺便把文件上传这块的功能开发了。

为了处理文件上传,Nest 提供了一个内置的基于 multer 中间件包的 Express 模块。Multer 处理以 multipart/form-data 格式发送的数据,该格式主要用于通过 HTTP POST 请求上传文件。

安装依赖

代码语言:powershell
复制
pnpm add @nestjs/platform-express multer uuid

我们需要安装三个包,前面两个是文件上传必须的,后面的 uuid 是生成文件名的,如果不需要可以不安装。

单个文件

当我们要上传单个文件时, 我们只需将 FileInterceptor() 与处理程序绑定在一起, 然后使用 @UploadedFile() 装饰器从 request 中取出 file

代码语言:ts
复制
@Post('upload')

@UseInterceptors(FileInterceptor('file'))

uploadFile(@UploadedFile() file: Express.Multer.File) {

 console.log(file);

}

FileInterceptor() 装饰器是 @nestjs/platform-express 包提供的, @UploadedFile() 装饰器是 @nestjs/common 包提供的。

FileInterceptor() 接收两个参数:

  1. fieldName:指向包含文件的 HTML 表单的字段
  2. options:类型为 MulterOptions 。这个和被传入 multer 构造函数 (此处有更多详细信息) 的对象是同一个对象。

文件数组

文件数组使用 FilesInterceptor() 装饰器,这个装饰器有三个参数:

  1. fieldName:同上
  2. maxCount:可选的数字,定义要接受的最大文件数
  3. options:同上
代码语言:ts
复制
@Post('upload')

@UseInterceptors(FilesInterceptor('files'))

uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) {

 console.log(files);

}

多个文件

要上传多个文件(全部使用不同的键),请使用 FileFieldsInterceptor() 装饰器。这个装饰器有两个参数:

  1. uploadedFields:对象数组,其中每个对象指定一个必需的 name 属性和一个指定字段名的字符串值
  2. options:同上
代码语言:ts
复制
@Post('upload')

@UseInterceptors(FileFieldsInterceptor([

 { name: 'avatar', maxCount: 1 },

 { name: 'background', maxCount: 1 },

]))

uploadFile(@UploadedFiles() files: { avatar?: Express.Multer.File[], background?: Express.Multer.File[] }) {

 console.log(files);

}

新建模块 module

1、 使用生成器创建模块,也可以自己手动创建

代码语言:powershell
复制
 nest g resource file-upload

2、 file-upload.service.ts,服务层为空即可

代码语言:ts
复制
 import { Injectable } from '@nestjs/common';

 @Injectable()

 export class FileUploadService { }

3、 file-upload.controller.ts,当我们要上传单个文件时, 我们只需将 FileInterceptor() 与处理程序绑定在一起, 然后使用 @UploadedFile() 装饰器从 request 中取出 file

代码语言:ts
复制
 import { Controller, Post, Req, UploadedFile, UseInterceptors } from '@nestjs/common';
 import { FileInterceptor } from '@nestjs/platform-express';
 import { ApiBody, ApiConsumes } from '@nestjs/swagger';
 import { Request } from 'express';
 import { responseMessage } from '@/utils';
 import { FileUploadDto } from './dto';

 @Controller('upload')

 export class FileUploadController {

 @UseInterceptors(FileInterceptor('file'))

 @Post('single-file')

 @ApiConsumes('multipart/form-data')

 @ApiBody({

 description: '单个文件上传',

 type: FileUploadDto,

 })

   uploadFile(@UploadedFile() file: Express.Multer.File, @Req() req: Request): Api.Common.Response<Express.Multer.File> {

 // 获取客户端域名端口_

 const hostname = req.headers['x-forwarded-host'] || req.hostname;

 const port = req.headers['x-forwarded-port'] || req.socket.localPort;

 const protocol = req.headers['x-forwarded-proto'] || req.protocol;

 file.path = `${protocol}://${hostname}:${port}/static${file.path.replace(/\\/g, '/').replace(/upload/g, '')}`;

 return responseMessage(file);

 }

 }

4、 file-upload.module.ts,我们在 module 层注册并根据实际情况配置文件上传路径

代码语言:ts
复制
 import { Module } from '@nestjs/common';
 import { MulterModule } from '@nestjs/platform-express';
 import dayjs from 'dayjs';
 import { diskStorage } from 'multer';
 import { v4 as uuidv4 } from 'uuid';
 import { checkDirAndCreate } from '@/utils';
 import { FileUploadController } from './file-upload.controller';
 import { FileUploadService } from './file-upload.service';

 @Module({

 imports: [
 MulterModule.registerAsync({
 useFactory: async () => ({
 limits: {
 fileSize: 1024 * 1024 * 5, // 限制文件大小为 5MB
 },

 storage: diskStorage({

 // 配置文件上传后的文件夹路径

 destination: (_, file, cb) => {

 // 定义文件上传格式

 const allowedImageTypes = ['gif', 'png', 'jpg', 'jpeg', 'bmp', 'webp', 'svg', 'tiff']; // 图片

 const allowedOfficeTypes = ['xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx', 'pdf', 'txt', 'md', 'csv']; // office

 const allowedVideoTypes = ['mp4', 'avi', 'wmv']; // 视频
 const allowedAudioTypes = ['mp3', 'wav', 'ogg']; // 音频

 // 根据上传的文件类型将图片视频音频和其他类型文件分别存到对应英文文件夹

 const fileExtension = file.originalname.split('.').pop().toLowerCase();

 let temp = 'other';
 if (allowedImageTypes.includes(fileExtension)) {
 temp = 'image';
 } else if (allowedOfficeTypes.includes(fileExtension)) {
 temp = 'office';
 } else if (allowedVideoTypes.includes(fileExtension)) {
 temp = 'video';
 } else if (allowedAudioTypes.includes(fileExtension)) {
 temp = 'audio';
 }

 // 文件以年月命名文件夹

 const filePath = `upload/${temp}/${dayjs().format('YYYY-MM')}`;
 checkDirAndCreate(filePath); // 判断文件夹是否存在,不存在则自动生成
 return cb(null, `./${filePath}`);
 },

 filename: (_, file, cb) => {

 // 使用随机 uuid 生成文件名

 const filename = `${uuidv4()}.${file.mimetype.split('/')[1]}`;
 return cb(null, filename);
 },
 }),
 }),
 }),
 ],

 controllers: [FileUploadController],

 providers: [FileUploadService],

 })

 export class FileUploadModule { }

效果演示

我们使用 postman 模拟上传:

上传后的文件夹结构:

配置文件访问

我们上传完成后的地址,比如:http://localhost:3000/static/image/2024-07/68bfe42a-06f2-462f-91fa-626f52f04845.jpeg 是不能直接访问的,我们还需要在 main.ts 里面配置:

代码语言:ts
复制
import { NestFactory } from '@nestjs/core';

import { NestExpressApplication } from '@nestjs/platform-express';

import \* as express from 'express';

import { join } from 'path';

import { AppModule } from './app.module';

async function bootstrap() {

 const app = await NestFactory.create<NestExpressApplication>(AppModule);

 _// 配置文件访问  文件夹为静态目录,以达到可直接访问下面文件的目的_

 const rootDir = join(\_\_dirname, '..');

 app.use('/static', express.static(join(rootDir, '/upload')));

 await app.listen(3000);

}

bootstrap();

配置完成就能正常访问文件了。

总结

我只能了单个文件上传,文件数组和多个文件上传也是一样的道理,大家可自行实现。

不过现在公司业务很少用上传到服务器本地的,业务量大的话会对服务器造成压力,一般这种适合个人站点、博客使用,这里我们当做学习就行。

GithubVue3 Admin

官网文档file-upload

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 安装依赖
  • 单个文件
  • 文件数组
  • 多个文件
  • 新建模块 module
  • 效果演示
  • 配置文件访问
  • 总结
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档