Hapi.js 文件上传完全指南
Hapi.js 以配置驱动处理文件上传:payload 选项控制解析行为与大小限制。本文讲解单/多文件上传、流式写盘、文件校验与进度追踪的生产级实现。
Hapi 的文件上传通过路由的 payload 配置完成:output: 'stream' 拿到流、maxBytes 限制大小、parse: true 自动解析 multipart。流式写盘(pipe 到 fs)是内存安全的关键,所有大小与类型限制都在路由配置里声明——这正是 Hapi 配置驱动哲学的体现。
Hapi 的 payload 哲学
Hapi 把请求体的处理收进路由配置:解析方式(stream/data/file)、大小上限、超时、多部分行为全部声明式。文件上传的安全基线因此写在路由定义里,一眼可审计。
基础上传实现
const Hapi = require('@hapi/hapi');
const fs = require('fs');
const path = require('path');
const { pipeline } = require('stream/promises');
const server = Hapi.server({ port: 3000 });
server.route({
method: 'POST',
path: '/upload',
options: {
payload: {
output: 'stream', // 流式获取文件
parse: true, // 解析 multipart
maxBytes: 10 * 1024 * 1024, // 10MB 上限
multipart: true,
},
},
handler: async (request, h) => {
const file = request.payload.file;
const filename = `${Date.now()}-${path.basename(file.hapi.filename)}`;
await pipeline(file, fs.createWriteStream(`uploads/${filename}`));
return h.response({ filename }).code(201);
},
});
output 三种模式:stream(流,大文件唯一正确选择)、data(整个 Buffer 进内存,仅限小文件)、file(直接落临时文件)。
文件校验体系
Hapi 配置层之外,业务校验在 handler 内组合:
const ALLOWED = ['image/jpeg', 'image/png'];
// MIME 类型(hapi 从请求头解析)
if (!ALLOWED.includes(file.hapi.headers['content-type'])) {
return h.response({ message: '文件类型不支持' }).code(415);
}
完整校验链:content-type 头 → 扩展名白名单 → 文件头嗅探(magic bytes)。maxBytes 超限会收到 413 响应,错误处理在 onPreResponse 扩展点统一格式化。
多文件与附加字段
同一个 input 多选或多个文件字段:
handler: async (request, h) => {
const { photos, caption } = request.payload;
const files = Array.isArray(photos) ? photos : [photos];
const saved = [];
for (const file of files) {
const filename = `${crypto.randomUUID()}-${path.basename(file.hapi.filename)}`;
await pipeline(file, fs.createWriteStream(`uploads/${filename}`));
saved.push(filename);
}
return h.response({ saved, caption }).code(201);
}
multipart 中的文本字段(caption)与文件字段在 payload 里共存,直接取用。
进度追踪
流式模式的福利:file.on('data') 累计字节数即可算进度百分比,配合 SSE 或 WebSocket 推给前端。超大文件场景建议直接改用分片上传协议(tus)或对象存储预签名直传,hapi 只接元数据回调。
跟踪上传失败发生的阶段
Hapi.js 文件上传完全指南需要把请求校验、文件接收、内容检查和对象存储写入区分开。向观测云上报脱敏的失败阶段、文件大小区间和耗时,结合已插桩的存储调用排查超时。不要把原始文件内容、上传令牌或个人文件名写入日志;监控用于定位故障,不代替文件类型验证、鉴权与恶意内容检测。
接入文档:日志采集 · Node.js APM 接入 · 监控器与告警策略。
常见问题(FAQ)
Q:Hapi 上传和 multer(Express 系)有什么本质区别?
理念差异:multer 是中间件(处理完挂到 req.file),Hapi 是框架内建 payload 语义(配置即行为)。功能等价,Hapi 的声明式配置在安全审计时更友好。
Q:413 Payload Too Large 的响应能自定义吗?
用 server.ext('onPreResponse', ...) 拦截 Boom 错误,改写为统一错误格式;同时在 payload 配置里设 failAction 控制超限行为。
Q:上传目录如何规划?
按日期/租户分目录(uploads/2026/10/),文件名 UUID 化;上传目录与应用代码分离挂载,Web 层不直接暴露——下载走鉴权接口或对象存储签名 URL。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。