html-pdf场景下HTML img src绝对路径加载失效问题排查
问题核心原因
html-pdf 底层依赖 PhantomJS 实现HTML转PDF,运行在Node侧无头环境,和浏览器的资源加载逻辑完全不同,之前的写法踩了几个典型坑:
/templates/header-footer.png是Web服务的根路径写法,PhantomJS不会请求Next.js的静态资源路由,会直接从本地磁盘根目录查找文件,必然找不到。- 直接传入磁盘绝对路径没有加
file://协议头时,多数版本的PhantomJS无法正确识别本地资源。 - TypeScript编译默认只会处理TS/JS文件,不会自动把
src/templates下的静态图片拷贝到dist目录,很多时候路径拼对了,但dist目录里根本没有对应图片文件。 - 模块被Next.js引入时,如果Next.js做了bundle打包或者启用ESM,
__dirname的指向可能和本地开发时的预期不一致,直接拼路径容易出错。
排查顺序
按以下步骤逐一验证,不要跳步:
- 先检查构建产物:打开模块输出的
dist目录,确认templates/header-footer.png真实存在,不存在的话先解决静态资源拷贝问题。 - 验证路径有效性:在生成PDF的逻辑里打印你拼接出的图片绝对路径,直接在系统文件管理器中输入该路径,确认能正常打开图片,排除路径拼接错误。
- 验证路径格式:确认传入HTML的资源路径带合法协议头,本地文件需要用
file://协议,或者直接用base64内嵌格式。
可落地的解决方案
二选一即可,优先推荐方案2,兼容性最好。
方案1:file协议绝对路径(适合常规Node部署环境)
首先解决构建时静态资源不拷贝的问题,在package.json中添加postbuild钩子,TS编译完成后自动把图片目录拷贝到dist目录:
{ "scripts": { "build": "tsc", "postbuild": "cp -r src/templates dist/templates" } }
拼接路径时不要直接拼磁盘路径,用Node原生的pathToFileURL方法转成标准file协议地址,自动适配Windows/macOS/Linux的路径格式:
const path = require('path'); const { pathToFileURL } = require('url'); const imgAbsPath = path.join(__dirname, "templates", "header-footer.png"); // 转成PhantomJS可识别的合法资源地址 const imgSrc = pathToFileURL(imgAbsPath).href; // 将imgSrc传入模板替换img标签的src属性即可 const reportHtml = `<img src="${imgSrc}" alt="页眉页脚" />`
注意:如果Next.js部署在Serverless环境(比如Vercel、Netlify边缘节点),不要用这个方案,Serverless环境文件系统不稳定,直接选方案2。
方案2:Base64内嵌(全环境兼容,零路径问题)
直接读取图片内容转成Base64字符串内嵌到HTML中,完全不依赖外部文件加载,不受部署环境、打包配置、工作目录影响,是最稳妥的方案:
const path = require('path'); const fs = require('fs'); const imgAbsPath = path.join(__dirname, "templates", "header-footer.png"); const imgBuffer = fs.readFileSync(imgAbsPath); // 注意MIME类型和图片格式匹配:png用image/png,jpg用image/jpeg const imgSrc = `data:image/png;base64,${imgBuffer.toString('base64')}`; const reportHtml = `<img src="${imgSrc}" alt="页眉页脚" />`
这个方案唯一的缺点是大图片会增加HTML体积,但报告用的页眉页脚、logo类图片通常体积很小,不会有性能问题。
避坑提示
- 不要用
./templates/xxx.png这类相对路径,html-pdf运行时的工作目录不固定,相对路径大概率指向错误位置。 - 如果你用Rollup/Webpack等工具打包Node模块,不要把图片资源打包进JS Bundle,要么配置静态资源输出规则,要么直接用Base64方案绕过资源路径问题。
- 如果模板是独立的HTML文件,不要在静态文件里写死图片路径,渲染模板时动态替换src值即可。
内容的提问来源于stack exchange,提问作者ValenciaHQ
相关产品推荐
相关产品推荐

