【问题标题】:Upload file using nestjs and multer使用nestjs和multer上传文件
【发布时间】:2018-08-12 06:12:35
【问题描述】:

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

【问题讨论】:

    标签: nestjs


    【解决方案1】:

    @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 将具有filenamedestinationpath

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

    还可以使用@UploadedFilesFilesInterceptor(复数)来处理多个文件

    【讨论】:

    • const randomName 在做什么?
    • 一种生成随机 32 个字符的疯狂方式。你最好用npmjs.com/package/uuid
    • @VictorIvens 能否请您展示如何使用最新软件包更改文件名。谢谢
    • 如何动态重命名图片?假设当我们创建一个新用户时,我们需要上传个人资料图片并使用 userId 重命名图片?
    【解决方案2】:

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

    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)
    }
    

    【讨论】:

    • 很好的解决方案!我喜欢你把所有的部分放在一起
    • multerConfig.dest 未定义,因为env 没有在第一次加载。
    • @DucTrungMai 确保在应用程序启动之前加载 env 文件。要检查这一点,请在 main.ts 文件的开头添加此导入 import * as dotenv from 'dotenv'; 这可确保在加载应用上下文之前 env 可供使用。
    【解决方案3】:

    使用 Multer 选项实现最简洁的实现

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

    然而,我在代码中发现了以下问题

    • 在最新版本的 NestJS 中,@nestjs/common 包中不存在名为 FileInterceptor 的导入。
    • 代码在我看来有点太杂乱了。

    所以,为了简化事情,我想出了以下解决方案。

    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)}`;
    }
    
    

    your-controller.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;
      }
    }
    

    【讨论】:

    • 我对您的解决方案有疑问。 ``` @UseInterceptors( FileInterceptor( 'file', { storage }, ), ) ``` 抛出错误:Argument type Type<NestInterceptor> is not assignable to parameter type NestInterceptor | Function
    【解决方案4】:

    **

    2021 年更新

    **

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

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

    【讨论】:

      【解决方案5】:

      如果您通过 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,
          };
        }
      

      【讨论】:

        【解决方案6】:
            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);
            }
        

        【讨论】:

          【解决方案7】:

          一个简单的方法是使用控制器。您需要定义一个上传控制器并将其添加到您的 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));
          

          【讨论】:

            猜你喜欢
            • 2021-11-09
            • 2016-03-09
            • 2016-10-22
            • 2016-06-02
            • 2021-01-03
            • 1970-01-01
            • 2019-07-31
            • 1970-01-01
            相关资源
            最近更新 更多