FOP 2.1中fo:external-graphic无法加载本地服务器图片求助
解决FOP 2.1加载本地服务器图片URL空白的问题
根据你描述的情况,公网图片能正常渲染但本地服务器的图片URL在PDF里显示空白,结合日志里的Image not found报错,核心问题大概率是FOP请求本地接口时缺少必要的身份凭据,或者对本地HTTPS配置不兼容——毕竟公网图片是匿名可访问的,但你的/api/download/image/接口显然需要身份验证,而FOP默认的HTTP请求不会自动携带会话信息(比如Cookie、Token)。下面是几个针对性的解决方案:
1. 给FOP配置自定义HTTP请求处理器,携带身份凭据
FOP默认的资源加载器不会带上你的应用会话信息,所以需要自定义ResourceResolver,让它在请求本地服务器时添加认证头:
实现自定义资源解析器
import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.xmlgraphics.io.Resource; import org.apache.xmlgraphics.io.ResourceResolver; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; public class AuthenticatedResourceResolver implements ResourceResolver { private final ResourceResolver defaultResolver; private final String sessionCookie; // 或者你的认证Token public AuthenticatedResourceResolver(ResourceResolver defaultResolver, String sessionCookie) { this.defaultResolver = defaultResolver; this.sessionCookie = sessionCookie; } @Override public Resource getResource(URI uri) throws IOException { // 只针对本地服务器URL做特殊处理 if ("https".equals(uri.getScheme()) && uri.getHost().equals("localhost")) { HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); // 添加上你的会话Cookie,或者替换成Authorization Token conn.setRequestProperty("Cookie", "JSESSIONID=" + sessionCookie); conn.connect(); return new Resource(conn.getInputStream(), conn.getContentType()); } // 其他URL用默认处理器 return defaultResolver.getResource(uri); } @Override public void putResource(URI uri, InputStream is) throws IOException { defaultResolver.putResource(uri, is); } }
在FOP初始化时配置该解析器
// 原有的FopFactory初始化代码 FopFactory fopFactory = FopFactory.newInstance(new java.net.URI("."), inStr); // 获取默认资源解析器,替换为自定义的 ResourceResolver defaultResolver = ResourceResolverFactory.createDefaultResourceResolver(); // 传入你的会话ID或认证Token String yourSessionId = "your-active-session-id"; fopFactory.setResourceResolver(new AuthenticatedResourceResolver(defaultResolver, yourSessionId)); // 后续的PDF生成逻辑保持不变
2. 兼容本地自签名HTTPS证书
如果你的本地服务器用的是自签名SSL证书,FOP的默认HTTP客户端会拒绝信任它,导致请求失败。可以临时添加证书信任(生产环境建议使用正规CA签发的证书):
在自定义ResourceResolver的HTTP请求代码前,添加以下SSL信任配置:
// 跳过SSL证书验证(仅测试环境使用) TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {} public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // 跳过主机名验证 HostnameVerifier allHostsValid = (hostname, session) -> true; HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
3. 提前下载图片到内存,用Base64嵌入FO模板
如果HTTP请求配置太繁琐,可以先通过你的应用代码获取图片字节数组,转成Base64后直接嵌入FO模板,避免FOP发起外部请求:
步骤1:在生成XML时获取图片Base64
// 调用你的接口获取图片字节数组 byte[] imageBytes = getFileAsResponseEntity(true, 1329, 1304).getBody(); // 转成Base64字符串 String base64Image = Base64.getEncoder().encodeToString(imageBytes); // 将base64Image存入XML的对应字段,比如<imageBase64>{base64Image}</imageBase64>
步骤2:修改XSL FO模板的图片引用
<xsl:choose> <xsl:when test="imageBase64 != ''"> <!-- 用Base64嵌入图片 --> <fo:external-graphic src="data:image/jpeg;base64,{imageBase64}" content-width="scale-down-to-fit" width="100%"/> </xsl:when> <xsl:otherwise> <!-- fallback到外部URL --> <fo:external-graphic src="{$ImagePath}" content-width="scale-down-to-fit" width="100%"/> </xsl:otherwise> </xsl:choose>
4. 验证本地接口的响应完整性
先用curl或Postman测试本地接口,确保返回的图片数据完整可打开:
curl -v "https://localhost:4200/api/download/image/?fixedPrice=true&productId=1329&fileId=1304" --output test.jpg
如果test.jpg能正常打开,说明接口返回没问题,问题还是出在FOP的请求配置上。
内容的提问来源于stack exchange,提问作者Marko Taht
相关产品推荐
相关产品推荐

