8

由于nestjs 是一个express 应用程序,因此可以使用任何库来处理使用nest 的上传,并且由于它提供了Midlewares,因此也可以使用multer。我的问题是:使用nestjs 处理文件上传的最佳方法是什么?

4

7 回答 7

28

正如@Kamyl 在问题https://github.com/nestjs/nest/issues/262上所告知的那样,因为v4.6.0可以使用通用文件拦截器将使用 multer 的文件上传到 nestjs。

import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'

... 

@UseInterceptors(FileInterceptor('file'))
async upload( @UploadedFile() file) {
  console.log(file)
}

这样,变量file将具有buffer


使用 Multer 选项

它还需要字段名称作为第一个参数,然后是一个带有 Multer Options 的数组

import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'

...

@UseInterceptors(FileInterceptor('file', {
  storage: diskStorage({
    destination: './uploads'
    , filename: (req, file, cb) => {
      // Generating a 32 random chars long string
      const randomName = Array(32).fill(null).map(() => (Math.round(Math.random() * 16)).toString(16)).join('')
      //Calling the callback passing the random name generated with the original extension name
      cb(null, `${randomName}${extname(file.originalname)}`)
    }
  })
}))
async upload( @UploadedFile() file) {
  console.log(file)
}

这样,变量file将具有filename,destinationpath

destination来自 的参数也diskStorage可以是一个函数,其参数和期望回调与filename. 通过传递一个diskStorage文件将自动保存到目的地,并通知给定的文件名。

也可以使用@UploadedFilesand FilesInterceptor(复数)来处理多个文件

于 2018-03-04T13:43:50.663 回答
21

更简洁的方法是将配置提取到单独的文件中,然后在拦截器方法中调用它

import { extname } from 'path';
import { existsSync, mkdirSync } from 'fs';
import { diskStorage } from 'multer';
import { v4 as uuid } from 'uuid';
import { HttpException, HttpStatus } from '@nestjs/common';

// Multer configuration
export const multerConfig = {
    dest: process.env.UPLOAD_LOCATION,
};

// Multer upload options
export const multerOptions = {
    // Enable file size limits
    limits: {
        fileSize: +process.env.MAX_FILE_SIZE,
    },
    // Check the mimetypes to allow for upload
    fileFilter: (req: any, file: any, cb: any) => {
        if (file.mimetype.match(/\/(jpg|jpeg|png|gif)$/)) {
            // Allow storage of file
            cb(null, true);
        } else {
            // Reject file
            cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false);
        }
    },
    // Storage properties
    storage: diskStorage({
        // Destination storage path details
        destination: (req: any, file: any, cb: any) => {
            const uploadPath = multerConfig.dest;
            // Create folder if doesn't exist
            if (!existsSync(uploadPath)) {
                mkdirSync(uploadPath);
            }
            cb(null, uploadPath);
        },
        // File modification details
        filename: (req: any, file: any, cb: any) => {
            // Calling the callback passing the random name generated with the original extension name
            cb(null, `${uuid()}${extname(file.originalname)}`);
        },
    }),
};

然后像这样在拦截器下调用它

import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'
import { multerOptions } from 'src/config/multer.config';
...

@Post('/action/upload')
@UseInterceptors(FileInterceptor('file', multerOptions))
async upload( @UploadedFile() file) {
  console.log(file)
}
于 2019-01-24T09:16:14.513 回答
5

使用 Multer 选项的最干净的实现

谢谢@VictorIvens 提供最好的答案。

但是,我在代码中发现了以下问题

  • 调用的导入在最新版本的 NestJSFileInterceptor中不存在。@nestjs/common
  • 代码在我看来有点太杂乱了。

因此,为了简化事情,我提出了以下解决方案。

storage.config.ts

export const storage = diskStorage({
  destination: "./uploads",
  filename: (req, file, callback) => {
    callback(null, generateFilename(file));
  }
});

function generateFilename(file) {
  return `${Date.now()}.${extname(file.originalname)}`;
}

你的控制器.controller.ts

import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFile
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";

import { diskStorage } from "multer";
import { extname } from "path";
import { storage } from "./storage.config"


@Controller()
export class YourController {
  @Post("upload") // API path
  @UseInterceptors(
    FileInterceptor(
      "file", // name of the field being passed
      { storage }
    )
  )
  async upload(@UploadedFile() file) {
    return file;
  }
}
于 2019-12-28T10:24:53.637 回答
1

**

2021 更新

**

现在要执行此操作,您需要像这样导入 FileInterceptor ...

import { FileInterceptor } from '@nestjs/platform-express';
于 2021-04-20T20:59:48.873 回答
0
    Create a helper.ts file that rename your file and contains path
        
    export class Helper {
            static customFileName(req, file, cb) {
              const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
              let fileExtension = "";
              if(file.mimetype.indexOf("jpeg") > -1){
                  fileExtension = "jpg"
              }else if(file.mimetype.indexOf("png") > -1){
                  fileExtension = "png";
              }
              const originalName = file.originalname.split(".")[0];
              cb(null, originalName + '-' + uniqueSuffix+"."+fileExtension);
            }
           
            static destinationPath(req, file, cb) {
              cb(null, 'uploads/')
            }
          }

code for controller

import { Helper } from '../service/Helper';
import { diskStorage } from 'multer';
import {FileInterceptor} from '@nestjs/platform-express'
import {Controller, Post, Body, UseInterceptors, UploadedFile} from '@nestjs/common'

    @Post('upload')
    @UseInterceptors(
        FileInterceptor('picture', {
            storage: diskStorage({
                destination: Helper.destinationPath,
                filename: Helper.customFileName,
            }),
        }),
    )

    uploadFile(@UploadedFile() file: Express.Multer.File) {
    console.log(file);
    }
于 2021-09-27T10:50:48.050 回答
0

如果您通过 API 调用从用户那里获取数据,您可以将数据保存为缓冲区并使用adm-zip访问内容。下面是nest.js 中的控制器方法实现。

@Post("/blackBoardUpload")
  @UseInterceptors(
    FileInterceptor('image', {
      storage: memoryStorage(),



      fileFilter: zipFileFilter,

    }),
  )
  async uploadedFile(@UploadedFile() file) {
    console.log(file)
    const response = {
      originalname: file.originalname,
      filename: file.filename,
    };
    var AdmZip = require('adm-zip');
    var zip = new AdmZip(file.buffer);

    var zipEntries = zip.getEntries();
    console.log(zipEntries.length);
    
    return {
      status: HttpStatus.OK,
      message: 'Received Zip file successfully!',
      data: response,
    };
  }
于 2021-02-18T15:50:29.317 回答
-2

一个简单的方法是使用控制器。您需要定义一个上传控制器并将其添加到您的 app.module 中,这是一个控制器应该是(后端)的示例:

@Controller()
export class Uploader {
  @Post('sampleName')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file) {
  // file name selection 
    const path = `desired path`;
    const writeStream = fs.createWriteStream(path);  
    writeStream.write(file.buffer);
    writeStream.end();
    return {
      result: [res],
    };
  }
}

并在前端通过 fetch 调用您的控制器:

    fetch('controller address', {
          method: 'POST',
          body: data,
        })
          .then((response) => response.json())
          .then((success) => {
            // What to do when succeed 
});
          })
          .catch((error) => console.log('Error in uploading file: ', error));
于 2019-03-05T13:37:59.513 回答