Search code examples
javascripttypescriptnestjsmulter

How to reuse mutler settings to specify a different folder for a route


I initialize MutlerModule to app.module:

MulterModule.registerAsync({
    imports: [ ConfigModule ],
    inject: [ ConfigService ],

    async useFactory(configService: ConfigService)
    {
        const uploadFolder = configService.get<IConfig[ "uploadFolder" ]>("uploadFolder");

        return {
            storage: diskStorage({
                destination: uploadFolder,
                filename(req, file, cb)
                {
                    const filename = `${ Date.now() }-${ file.originalname }`;

                    cb(null, filename);
                }
            })
        };
    }
}),

In uploadFolder we have uploads. Further for some route I want to save not in uploads but in uploads/category, how can I do it without duplicating the code?

What I need to change somewhere that the files that are sent to this route go to uploads/category

@Controller("category")
export class CategoryController
{
    @UseInterceptors(FileInterceptor("image"))
    @Post()
    async create(
        @UploadedFile(
            new ParseFilePipeBuilder()
                .addFileTypeValidator({ fileType: ALLOWED_IMAGE_TYPES.join() })
                .addMaxSizeValidator({ maxSize: MAX_FILE_SIZE_BYTES })
                .build({
                    fileIsRequired: false,
                    errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY
                })
        )
        image
    )
    {
        console.log(image);
    }
}

Solution

  • Path Based Folder

    You can decide which folder to upload by passing a function to destination parameter :

    MulterModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      async useFactory(configService: ConfigService) {
        const baseUploadFolder = configService.get<string>('uploadFolder'); // Base folder from config
    
        return {
          storage: diskStorage({
            destination: (req, file, cb) => {
              // Add logic to determine folder dynamically
              const folder = req.path === "food" ? 'category/food' : 'default';
              const uploadFolder = `${baseUploadFolder}/${folder}`;
              
              // Ensure folder exists
              import('fs').then(fs => {
                if (!fs.existsSync(uploadFolder)) {
                  fs.mkdirSync(uploadFolder, { recursive: true });
                }
              });
    
              cb(null, uploadFolder);
            },
            filename: (req, file, cb) => {
              const filename = `${Date.now()}-${file.originalname}`;
              cb(null, filename);
            },
          }),
        };
      },
    });
    

    Using this you can apply check where to store files based upon the path you get request from user.

    Re-usable Multer Config way

    And if you want to handle this at controller level you can create a function to get multer configuration :

    import { diskStorage } from 'multer';
    import * as path from 'path';
    
    export function createMulterConfig(folder: string) {
      return {
        storage: diskStorage({
          destination: path.join(__dirname, '..', '..', 'upload', folder),
          filename: (req, file, cb) => {
            const filename = `${Date.now()}-${file.originalname}`;
            cb(null, filename);
          },
        }),
      };
    }
    
    

    In your controller, pass the specific folder name to the factory function and use it with FileInterceptor.

    import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
    import { FileInterceptor } from '@nestjs/platform-express';
    import { createMulterConfig } from './multer-config.factory';
    
    @Controller('category')
    export class CategoryController {
      @Post('default')
      @UseInterceptors(FileInterceptor('file', createMulterConfig('default')))
      uploadDefault(@UploadedFile() file: Express.Multer.File) {
        return { message: 'File uploaded to default folder', file };
      }
    
      @Post('category')
      @UseInterceptors(FileInterceptor('file', createMulterConfig('category')))
      uploadCategory(@UploadedFile() file: Express.Multer.File) {
        return { message: 'File uploaded to category folder', file };
      }
    }