在Hapijs v17中如何借助Inert实现文件强制下载?
在Hapi.js v17中使用Inert实现文件强制下载
你的思路完全正确——强制文件下载的核心就是给响应设置Content-Disposition头为attachment,不过我们可以优化代码的写法、安全性和健壮性,让它更实用。
先看优化后的完整代码:
const path = require('path'); const mime = require('mime-types'); // 可选,用于动态匹配不同文件的MIME类型 server.route({ method: 'GET', path: '/uploads/{file*}', handler: (req, h) => { // 构建绝对路径,避免因运行目录变化导致的文件找不到问题 const uploadsRootDir = path.resolve(__dirname, './uploads'); const targetFilePath = path.resolve(uploadsRootDir, req.params.file); // 安全防护:防止用户通过../等参数遍历服务器敏感目录 if (!targetFilePath.startsWith(uploadsRootDir)) { return h.response('Invalid file request').code(403); } // 处理文件名编码,解决中文、空格或特殊字符导致的浏览器解析乱码问题 const encodedFileName = encodeURIComponent(req.params.file); // 动态获取文件的MIME类型(如果只需要PDF,可直接写死为application/pdf) const fileContentType = mime.lookup(targetFilePath) || 'application/octet-stream'; return h.file(targetFilePath, { contentType: fileContentType, headers: { 'Content-Disposition': `attachment; filename="${encodedFileName}"` } }); }, options: { auth: false } });
关键细节解释:
- 绝对路径构建:用
path.resolve代替相对路径,确保服务器无论在哪个目录启动,都能精准找到上传文件的位置。 - 路径安全校验:添加了目录边界检查,避免恶意用户通过路径遍历攻击访问服务器上的敏感文件。
- 文件名编码:通过
encodeURIComponent处理文件名,解决包含特殊字符、中文时浏览器保存文件出现乱码的问题。 - 灵活的MIME类型:借助
mime-types包动态匹配文件类型,比硬编码application/pdf更适配多类型文件的下载场景;如果你的场景只需要PDF,直接保留固定值即可。 - 简洁的配置方式:直接在
h.file的options参数里配置响应头和内容类型,这是Hapi官方推荐的写法,比链式调用.header()更清晰易维护。
如果想保留你最初的链式写法,也是可行的,但要补上文件名编码的处理:
server.route({ method: 'GET', path: '/uploads/{file*}', handler: (req, h) => { const encodedFileName = encodeURIComponent(req.params.file); return h.file(`./uploads/${req.params.file}`) .header('Content-Type', 'application/pdf') .header('Content-Disposition', `attachment; filename="${encodedFileName}"`); }, options: { auth: false } });
不过这种写法缺少路径安全校验,也存在相对路径的潜在问题,更推荐第一种优化后的方案。
内容的提问来源于stack exchange,提问作者Ali Hesari
相关产品推荐
相关产品推荐

