Nestjs上传图片/视频至服务器(支持多图片+多视频)

思考:数据库不适合使用blob来存储图片视频等,我们可以在用户上传静态资源时,后端将静态资源存储在服务器文件夹中,使用时间戳方式重新命名防止资源重名覆盖,将资源路径存储在数据库中。阅读本文你将会使用nestjs搭建服务器上传静态资源至mysql数据库

   首先我们先在项目根目录下创建upload模块 

nest g resource upload
情景1:用户需要上传单张图片。
step1 首先在生成的upload.module.ts模块中指定存储路径,destination为图片存储路径,filename是将时间戳重命名,extname截取后缀拼接。

将图片存储在images文件夹下,并且不会重名覆盖。

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



@Module({
  imports: [
    MulterModule.register({ 
      storage: diskStorage({
        destination: join(__dirname, "../images"),
        filename: (_, file, callback) => {
          const fileName = `${new Date().getTime() + extname(file.originalname)}`
          return callback(null, fileName)
        }
      })
    }),
  ],
  controllers: [UploadController],
  providers: [UploadService],
})
export class UploadModule { }
 step2 main.ts中配置静态路径   prefix是虚拟前缀
 app.useStaticAssets(join(__dirname, 'images'), {
    prefix: '/xiaoluo/images'
  })

 step3 接受上传文件

在upload.controller.ts控制器中使用 UseInterceptors 装饰器  
FileInterceptor是单个文件  读取字段名称   参数 使用 UploadedFile 装饰器接受file 文件 、整理data返回给前端      注意 API_POST是服务器端口  3000

import { Controller, Get, Post, Body, Patch, Param, Delete, UploadedFile, UploadedFiles, UseInterceptors, HttpStatus } from '@nestjs/common';
import { UploadService } from './upload.service';
import { CreateUploadDto } from './dto/create-upload.dto';
import { UpdateUploadDto } from './dto/update-upload.dto';
import { FileInterceptor } from '@nestjs/platform-express';
import { API_PORT } from '@/config';
 
@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) { }

  @Post('album')
  @UseInterceptors(FileInterceptor('file'))
  upload(@UploadedFile() file) {
    console.log(file)
    const data = {
      fileName: file.filename,
      imgURL: `http://127.0.0.1:${API_PORT}/xiaoluo/images/${file.filename}`,
      size: file.size,
      type: 'image'
    }
    return {
      data,
      code: HttpStatus.OK,
      msg: '上传成功'
    }
  }
}
step4 测试 

apifox测试上传单张图片  实际请求的是 http://localhost:3000/upload/album

在这里可看到上传成功,去服务端dist文件夹下的images文件查看上传的图片,通过配置好的路径我们也可以去浏览器访问地址 "http://localhost:3000/xiaoluo/images/1693990744385.webp"

服务器

浏览器


情景2:用户需要上传多张图片,多个视频

要求在服务器端静态资源区分开图片和视频

step1 main.ts文件夹中配置好静态路径

 现在修改一下静态路径配置  需要区分图片和视频      /xiaoluo   就相当于resources文件夹

 app.useStaticAssets(join(__dirname, 'resources'), {
    prefix: '/xiaoluo'
  })
step2  upload.module.ts中使用工厂函数 useFactory

这里添加了Math.random()*1E9 拼接时间戳命名, 是因为多个静态资源上传仅使用时间戳可能会导致重名从而导致资源覆盖,将图片视频资源区分放在resources文件夹下,使用fs来判断是否存在存储静态资源的文件夹,若不存在则创建一个,注意fs需要使用require引入

import { Module } from '@nestjs/common';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import { MulterModule } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname, join } from 'path';
const fs = require('fs');
@Module({
  imports: [
    MulterModule.registerAsync({
      useFactory: () => ({
        storage: diskStorage({
          destination: (req, file, callback) => {
            const uploadFile = file.mimetype.startsWith('video/') ? 'videos' : 'images';
            const folderPath = join(__dirname, '../resources/', uploadFile);
            fs.existsSync(folderPath) || fs.mkdirSync(folderPath, { recursive: true });
            return callback(null,folderPath)
          },
          filename: (req, file, callback) => {
            const fileName = `${new Date().getTime()}-${Math.random() * 1E9}${extname(file.originalname)}`
            return callback(null, fileName)
          },
        }),
      })

    }),
  ],
  controllers: [UploadController],
  providers: [UploadService],
})
export class UploadModule { }
step3  upload.controller.ts中返回上传成功数据至前端

图片与视频的区别可由 file.mimetype来区分 利用data数组返回各资源的信息 

  @Post('albums')
  @UseInterceptors(FilesInterceptor('files'))
  uoloadFiles(@UploadedFiles() files) {
    if (!files) return handleResult(HttpStatus.BAD_REQUEST, 'files不能为空')
    const data = [];
    files.forEach(file => {
      const isImage = file.mimetype.startsWith('image/');
      const isVideo = file.mimetype.startsWith('video/');

      if (isImage) {
        data.push({
          filename: file.filename,
          imgURL: `http://127.0.0.1:${API_PORT}/xiaoluo/images/${file.filename}`,
          size: file.size,
          type: 'image', // 添加文件类型标识
        });
      } else if (isVideo) {
        data.push({
          filename: file.filename,
          videoURL: `http://127.0.0.1:${API_PORT}/xiaoluo/videos/${file.filename}`,
          size: file.size,
          type: 'video', // 添加文件类型标识
        });
      }
    })
    return {
      data,
      msg: '上传成功',
      code: HttpStatus.OK
    };
  }
step 4 测试   

这里实际的请求为   "http://localhost:3000/upload/albums"  上传了3张照片,2个视频

我们可以看到resources下的  images有了三张新增的图片,videos下有了两个新增的视频,

它们是由事件戳+随机数组成重新命名的

贴出返回的data

{
    "data": {
        "data": [
            {
                "filename": "1694055101271-447801383.86596245.mp4",
                "videoURL": "http://127.0.0.1:3000/xiaoluo/videos/1694055101271-447801383.86596245.mp4",
                "size": 2133610,
                "type": "video"
            },
            {
                "filename": "1694055101290-927929695.0190749.mp4",
                "videoURL": "http://127.0.0.1:3000/xiaoluo/videos/1694055101290-927929695.0190749.mp4",
                "size": 11591089,
                "type": "video"
            },
            {
                "filename": "1694055101374-116357625.88050213.webp",
                "imgURL": "http://127.0.0.1:3000/xiaoluo/images/1694055101374-116357625.88050213.webp",
                "size": 18176,
                "type": "image"
            },
            {
                "filename": "1694055101375-88369715.42459083.webp",
                "imgURL": "http://127.0.0.1:3000/xiaoluo/images/1694055101375-88369715.42459083.webp",
                "size": 9866,
                "type": "image"
            },
            {
                "filename": "1694055101375-343681736.23349094.jpg",
                "imgURL": "http://127.0.0.1:3000/xiaoluo/images/1694055101375-343681736.23349094.jpg",
                "size": 25733,
                "type": "image"
            }
        ],
        "msg": "上传成功",
        "code": 200
    },
    "success": true,
    "message": "成功"
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以使用 `multer` 库来上传多张图片。以下是在 NestJS 中使用 `multer` 实现多张图片上传的示例代码: 1. 安装 `multer` 库: ``` npm install --save multer ``` 2. 在控制器中导入 `multer` 库: ``` import { Controller, Post, UploadedFiles, UseInterceptors } from '@nestjs/common'; import { FilesInterceptor } from '@nestjs/platform-express'; import { diskStorage } from 'multer'; import { editFileName } from './utils/file-upload.utils'; ``` 3. 创建一个上传文件的中间件: ``` export const multerOptions = { storage: diskStorage({ destination: './uploads', filename: editFileName, }), }; ``` 4. 创建一个上传文件的工具类: ``` export const editFileName = (req, file, callback) => { const name = file.originalname.split('.')[0]; const fileExtName = extname(file.originalname); const randomName = Array(4) .fill(null) .map(() => Math.round(Math.random() * 16).toString(16)) .join(''); callback(null, `${name}-${randomName}${fileExtName}`); }; ``` 5. 在控制器中使用 `@UseInterceptors()` 装饰器来启用 `FilesInterceptor` 中间件: ``` @Controller('upload') export class UploadController { @Post() @UseInterceptors(FilesInterceptor('files', 20, multerOptions)) async uploadMultipleFiles(@UploadedFiles() files) { console.log(files); const response = []; files.forEach((file) => { const fileReponse = { originalname: file.originalname, filename: file.filename, }; response.push(fileReponse); }); return response; } } ``` 6. 在路由中注册控制器: ``` import { Module } from '@nestjs/common'; import { UploadController } from './upload.controller'; @Module({ controllers: [UploadController], }) export class UploadModule {} ``` 这样,你就可以通过 POST 请求上传多张文件。在这个示例中,上传的文件将被保存在 `./uploads` 目录下。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值