Nest 文件上传 - 就是增强版的 el-upload

0 阅读2分钟

1. 回顾 Express 的 multer

上节我们学了 Express 的 multer:

// Express 写法
app.post('/upload', upload.single('file'), (req, res) => {
  req.file;  // 文件
  req.body;   // 其他字段
});

Nest 怎么写?


2. Nest 文件上传 - 三步走

第 1 步:安装类型包

npm install -D @types/multer

第 2 步:写接口

@Post('aaa')
@UseInterceptors(FileInterceptor('aaa', {
    dest: 'uploads'  // 保存目录
}))
uploadFile(
    @UploadedFile() file: Express.Multer.File,
    @Body() body
) {
    console.log('文件信息:', file);
    console.log('其他字段:', body);
}

第 3 步:前端上传

<input type="file" id="fileInput" />

<script>
const fileInput = document.querySelector('#fileInput');

fileInput.onchange = async () => {
  const data = new FormData();
  data.set('aaa', fileInput.files[0]);

  await axios.post('http://localhost:3000/aaa', data);
};
</script>

3. 对比 Express

ExpressNest说明
upload.single('field')FileInterceptor('field')单文件
upload.array('field', n)FilesInterceptor('field', n)多文件
upload.fields([...])FileFieldsInterceptor([...])多字段
upload.any()AnyFilesInterceptor()任意文件
req.file@UploadedFile()获取文件
req.files@UploadedFiles()获取文件数组
req.body@Body()获取其他字段

4. 多文件上传

后端

@Post('bbb')
@UseInterceptors(FilesInterceptor('bbb', 3, {
    dest: 'uploads'
}))
uploadFiles(
    @UploadedFiles() files: Array<Express.Multer.File>,
    @Body() body
) {
    console.log('文件列表:', files);
}

前端

// 传多个文件
[...fileInput.files].forEach(file => {
    data.append('bbb', file);
});

await axios.post('http://localhost:3000/bbb', data);

对比 Element Plus

<el-upload
  action="/api/upload"
  :auto-upload="false"
  :on-change="handleChange"
  multiple
>
</el-upload>

5. 不同字段传不同文件

场景:头像 + 附件分开传

后端

@Post('ccc')
@UseInterceptors(FileFieldsInterceptor([
    { name: 'avatar', maxCount: 1 },    // 头像,最多1个
    { name: 'attachments', maxCount: 3 } // 附件,最多3个
], {
    dest: 'uploads'
}))
uploadFileFields(
    @UploadedFiles() files: { avatar?: Express.Multer.File[], attachments?: Express.Multer.File[] },
    @Body() body
) {
    console.log('头像:', files.avatar);
    console.log('附件:', files.attachments);
}

前端

const data = new FormData();
data.append('avatar', avatarFile);
data.append('attachments', file1);
data.append('attachments', file2);

await axios.post('http://localhost:3000/ccc', data);

对比 el-upload 多文件上传

<el-upload
  action="/api/upload"
  :auto-upload="false"
  :file-list="fileList"
>
  <el-button>上传头像和附件</el-button>
</el-upload>

6. 任意文件上传

不知道有哪些字段会上传文件?

@Post('ddd')
@UseInterceptors(AnyFilesInterceptor({
    dest: 'uploads'
}))
uploadAnyFiles(
    @UploadedFiles() files: Array<Express.Multer.File>,
    @Body() body
) {
    console.log('所有文件:', files);
}

7. 文件信息详解

上传成功后,file 对象包含:

{
  fieldname: 'aaa',      // 字段名
  originalname: '1.png', // 原始文件名
  encoding: '7bit',      // 编码
  mimetype: 'image/png', // MIME 类型
  size: 1024,            // 文件大小(字节)
  destination: 'uploads', // 保存目录
  filename: 'xxx.png',   // 保存的文件名
  path: 'uploads/xxx.png' // 完整路径
}

对比前端的 file 对象

// 前端 file 对象
{
  name: '1.png',        // 文件名
  size: 1024,           // 大小
  type: 'image/png',     // 类型
  lastModified: 1234567890
}

8. 完整流程图

前端                                Nest
 ───                                ───
FormData + axios    ──────────►   @UseInterceptors
     │                            FileInterceptor
     │                                   │
     │   multipart/form-data             │
     │                                   │
     │                              @UploadedFile()
     │                                   ▼
     │                              保存到磁盘
     │                                   │
     ◄────────────────────────────────────┘
   响应

9. 总结

Nest 装饰器对应 Express作用
FileInterceptorupload.single()单文件上传
FilesInterceptorupload.array()多文件上传
FileFieldsInterceptorupload.fields()多字段多文件
AnyFilesInterceptorupload.any()任意文件
@UploadedFile()req.file获取单文件
@UploadedFiles()req.files获取文件数组
@Body()req.body获取其他字段

一句话总结

Nest 文件上传就是包装了 Express multer 的拦截器版本,用 @UseInterceptors + @UploadedFile 的方式接收文件,和前端的 el-upload 完美对应。

Express = 手写中间件 Nest = 装饰器 + 拦截器,更优雅!


觉得有帮助,点个赞再走~ 👋