Nest 如何使用 multer 文件上传?

33 阅读1分钟

新建

nest new nest-multer-upload-demo -p npm

npm install -D @types/multer

controller 更新

    @Post('aaa')
    @UseInterceptors(FileInterceptor('aaa', {
    dest: 'uploads'
    }))
        
    uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body) {
    console.log('body', body);
    console.log('file', file);
    }

使用 FileInterceptor 来提取 aaa 字段,然后通过 UploadedFile 装饰器把它作为参数传入

让 nest 服务支持跨域,单独跑 http-server 来提供静态服务

image.png

新建 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script>
</head>
<body>
    <input id="fileInput" type="file" multiple/>
    <script>
        const fileInput = document.querySelector('#fileInput');

        async function formData() {
            const data = new FormData();
            data.set('name','fuhao');
            data.set('age', 20);
            data.set('aaa', fileInput.files[0]);

            const res = await axios.post('http://localhost:3000/aaa', data);
            console.log(res);
        }

        fileInput.onchange = formData;
    </script>
</body>
</html>

上传

image.png

image.png

文件夹更新了

image.png

其他字段通过 @Body 装饰器获取

如果是多文件 ?

    @Post('bbb')
    @UseInterceptors(FilesInterceptor('bbb', 2, {
    dest: 'uploads'
    }))
        
    uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
    console.log('body', body);
    console.log('files', files);
 async function formData2() {
        const data = new FormData();
        data.set('name','fuhao');
        data.set('age', 20);
        [...fileInput.files].forEach(item => {
            data.append('bbb', item)
        })

        const res = await axios.post('http://localhost:3000/bbb', data, {
            headers: { 'content-type': 'multipart/form-data' }
        });
        console.log(res);
    }

        fileInput.onchange = formData2;

image.png

可以指定 storage

const multer  = require('multer')
const path = require('path')

export const storage = multer.diskStorage({
  destination(req, file, cb) {
    cb(null, 'uploads/')
  },
  filename(req, file, cb) {
    // 原始文件名
    const ext = path.extname(file.originalname)
    const name = path.basename(file.originalname, ext)

    // 自定义规则
    const newName = `${name}-${Date.now()}${ext}`

    cb(null, newName)
  }
})

controller中

    @Post('ddd')
    @UseInterceptors(AnyFilesInterceptor({
        storage: storage
    }))
        
    uploadAnyFiles(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
        console.log('body', body);
        console.log('files', files);
    }

上传试试

image.png

Nest 上传文件的方式和直接使用 multer 很像,它就是对 multer 做了一层简单的封装

如果需要对文件大小、类型做校验,可以用pipe

nest g pipe file-validation-pipe --no-spec --flat 
import { PipeTransform, Injectable, ArgumentMetadata, HttpException, HttpStatus } from '@nestjs/common';

@Injectable()
export class FileValidationPipe implements PipeTransform {
  transform(value: Express.Multer.File, metadata: ArgumentMetadata) {
    if(value.size > 20 * 1024) {
      throw new HttpException('文件大于 20k', HttpStatus.BAD_REQUEST);
    }
    return value;
  }
}

controller 使用

    @Post('eee')
    @UseInterceptors(FileInterceptor('aaa',{
        dest: 'uploads'
    }))
        
    uploadAnyFiles2(@UploadedFile(FileValidationPipe) files: Array<Express.Multer.File>, @Body() body) {
        console.log('body', body);
        console.log('files', files);
    }

index.html 中使用

        async function formData() {
            const data = new FormData();
            data.set('name','fuhao');
            data.set('age', 20);
            data.set('aaa', fileInput.files[0]);

            const res = await axios.post('http://localhost:3000/eee', data);
            console.log(res);
        }
        
        fileInput.onchange = formData;

image.png

这种 Nest 内部 用封装好可以直接使用

    @Post('fff')
    @UseInterceptors(FileInterceptor('aaa', {
        dest: 'uploads'
    }))
        
    uploadFile3(@UploadedFile(new ParseFilePipe({
        validators: [
        new MaxFileSizeValidator({ maxSize: 500 }),
        new FileTypeValidator({ fileType: 'image/jpeg' }),
        ],
    })) file: Express.Multer.File, @Body() body) {
        console.log('body', body);
        console.log('file', file);
    }

image.png

image.png

image.png

自定义校验

import { FileValidator } from "@nestjs/common";

export class MyFileValidator extends FileValidator{
    constructor(options) {
        super(options);
    }

    isValid(file: Express.Multer.File): boolean | Promise<boolean> {
        if(file.size > 50000) {
            return false;
        }
        return true;
    }
    buildErrorMessage(file: Express.Multer.File): string {
        return `文件 ${file.originalname} 大小超出 50k`;
    }
}

image.png

image.png

nice!