Fastify 文件上传完全指南

Fastify 用 @fastify/multipart 处理文件上传。本文讲解流式上传、文件类型与大小校验、多文件处理与安全防护(大小限制、MIME 嗅探、文件名清洗)。

最佳实践
应用接口与数据存储连接插画

Fastify 文件上传用官方 @fastify/multipart 插件:req.file() 获取单个上传文件,返回流式对象——用管道写盘而非读进内存,配合 limits 限制文件大小、fileFilter 校验 MIME 类型,即可构建安全的生产级上传接口。

安装与基础配置

npm install @fastify/multipart
const fastify = require('fastify')();

fastify.register(require('@fastify/multipart'), {
  limits: {
    fileSize: 10 * 1024 * 1024,   // 单文件最大 10MB
    files: 5,                      // 单请求最多 5 个文件
  },
});

limits 是安全第一道闸:不设大小限制的上传接口等于欢迎 DoS(恶意大文件撑爆磁盘/内存)。

流式上传:永远不要全读进内存

const { pipeline } = require('stream/promises');
const fs = require('fs');
const path = require('path');

fastify.post('/upload', async (req, reply) => {
  const data = await req.file();

  const filename = `${Date.now()}-${path.basename(data.filename)}`;
  await pipeline(data.file, fs.createWriteStream(`uploads/${filename}`));

  return { filename, mimetype: data.mimetype };
});

data.file 是可读流,pipeline 边收边写——10GB 文件内存占用也是恒定。千万别 await data.toBuffer(),那是内存自杀。

校验:MIME、扩展名与真实内容

const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];

fastify.post('/avatar', async (req, reply) => {
  const data = await req.file();

  if (!ALLOWED_TYPES.includes(data.mimetype)) {
    return reply.code(415).send({ message: '仅支持 JPG/PNG/WebP' });
  }
  // ...
});

三层校验纪律:MIME 类型(客户端声明,不可全信)→ 扩展名白名单 → 文件头嗅探(读取前几个字节验证真实格式,防伪装)。文件名必须清洗:path.basename 去路径穿越,随机前缀防覆盖与猜测。

多文件上传

fastify.post('/gallery', async (req, reply) => {
  const saved = [];
  for await (const part of req.files()) {
    if (!part.mimetype.startsWith('image/')) continue;
    const filename = `${crypto.randomUUID()}-${path.basename(part.filename)}`;
    await pipeline(part.file, fs.createWriteStream(`uploads/${filename}`));
    saved.push(filename);
  }
  return { saved };
});

req.files() 异步迭代多部分表单——注意超限部分会带 part.file.truncated 标记,要检查并拒绝不完整文件。

跟踪上传失败发生的阶段

Fastify 文件上传完全指南需要把请求校验、文件接收、内容检查和对象存储写入区分开。向观测云上报脱敏的失败阶段、文件大小区间和耗时,结合已插桩的存储调用排查超时。不要把原始文件内容、上传令牌或个人文件名写入日志;监控用于定位故障,不代替文件类型验证、鉴权与恶意内容检测。

接入文档:日志采集 · Node.js APM 接入 · 监控器与告警策略。

常见问题(FAQ)

Q:上传大文件超时怎么办?

两端都要放宽:Fastify 的 connectionTimeout 与反代(Nginx 的 proxy_read_timeout + client_max_body_size)。百 MB 级以上建议改用分片上传或对象存储预签名 URL 直传——应用服务器不该扛大文件流量。

Q:上传后如何做图片处理?

流式接 sharp:pipeline(data.file, sharp().resize(800).webp(), fs.createWriteStream(out))——边下载边压缩,内存依然恒定。

Q:生产环境文件存哪?

本地磁盘只适合开发。生产用对象存储(S3 兼容),上传链路改为"客户端直传预签名 URL"或"服务端流式转发到 S3",应用服务器不持有文件。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台