【Nest】图片(文件)上传

一、环境准备

  1. 安装依赖
    需安装 @nestjs/platform-express(Nest自带) 和 multer,部分场景还需 @types/multer(类型支持):
    npm install multer @types/multer
    

二、配置 Multer

Multer 用于处理 multipart/form-data 类型的请求,支持两种存储方式:
内存存储:适合临时处理文件(如直接上传到云存储):

import { MulterModule } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';

@Module({
  imports: [MulterModule.register({ storage: memoryStorage() })],
})

磁盘存储:保存文件到本地目录:

import { diskStorage } from 'multer';
import { join } from 'path';

MulterModule.register({
  storage: diskStorage({
    destination: join(__dirname, 'uploads'), // 保存路径
    filename: (req, file, cb) => {
      const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
      const ext = extname(file.originalname);
      cb(null, `${file.fieldname}-${uniqueName}${ext}`);
    },
  }),
})

代码实现

nest g res upload

在这里插入图片描述

upload.module.ts

import { join, extname } from 'node:path'
import { Module } from '@nestjs/common';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import {diskStorage} from "multer";
import {MulterModule} from "@nestjs/platform-express";

@Module({
  imports: [  MulterModule.register({
    storage: diskStorage({
      destination: join(__dirname, '../images'), // 保存路径
      filename: (req, file, cb) => {
        const uniqueName = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
        const ext = extname(file.originalname);
        cb(null, `${file.fieldname}-${uniqueName}${ext}`);
      },
    }),
  })],
  controllers: [UploadController],
  providers: [UploadService],
})
export class UploadModule {}


三、创建文件上传控制器

通过拦截器(Interceptors)和装饰器处理不同上传场景:

  1. 单个文件上传
    使用 FileInterceptor@UploadedFile()

    @Post('upload')
    @UseInterceptors(FileInterceptor('file')) // 'file'为前端表单字段名
    uploadFile(@UploadedFile() file: Express.Multer.File) {
      return { path: file.path };
    }
    
  2. 多文件或文件数组
    文件数组:使用 FilesInterceptor

    @UseInterceptors(FilesInterceptor('files', 5)) // 最多5个文件
    uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>) {}
    

    多个字段文件:使用 FileFieldsInterceptor

    @UseInterceptors(FileFieldsInterceptor([
      { name: 'avatar', maxCount: 1 },
      { name: 'background', maxCount: 1 },
    ]))
    
  3. 结合 DTO 验证
    若需同时传递其他表单数据(如 JSON 参数),可通过 @Body() 接收并校验:

    @Post()
    @UseInterceptors(FileInterceptor('file'))
    uploadFile(
      @UploadedFile() file: Express.Multer.File,
      @Body() dto: FileUploadDto // 自定义DTO类
    ) {}
    

代码实现:

upload.controller.ts

import {
  Controller,
  Post,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { FileInterceptor } from '@nestjs/platform-express';

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post('album')
  @UseInterceptors(FileInterceptor('file')) // file 为前端表单字段名
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    console.log(file);
    return {
      localPath: file.path,
      onlinePath: 'http://localhost:3000/images/' + file.filename,
    };
  }
}


四、静态文件访问配置

上传后需通过 HTTP 暴露静态资源,在 main.ts 中配置:

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

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.useStaticAssets(join(__dirname, 'uploads'), {
    prefix: '/static/', // 访问前缀(如 http://localhost:3000/static/filename.jpg)
  });
}

代码实现:

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'node:path';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.useStaticAssets(join(__dirname, 'images'), {
    prefix: '/images',
  });
  await app.listen(process.env.PORT ?? 3000);
}

bootstrap();

在这里插入图片描述

此时,我们通过本地路径和线上路径都可以访问到。

在这里插入图片描述


五、自定义文件名与路径

为避免文件名冲突,可通过以下方式生成唯一标识:
时间戳 + 随机数:如 Date.now() + '-' + Math.random()
UUID:安装 uuid 生成唯一字符串:

import { v4 as uuidv4 } from 'uuid';
filename: `${uuidv4()}${extname(file.originalname)}`

六、前端调用示例

前端通过 FormData 发送文件:

const formData = new FormData();
formData.append('file', fileInput.files[0]);
// 可选附加参数
formData.append('metadata', JSON.stringify({ userId: 123 }));

fetch('/upload', {
  method: 'POST',
  body: formData,
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秀秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值