FOP自定义ResourceResolver图片加载失败:基础URI配置疑问
问题:FOP自定义图片重定向后图片加载失败
我正在开发基于FOP的嵌入式自定义XML转PDF Java应用,需要在FOP中实现自定义图片重定向。
旧XSLT代码
<fo:block text-indent="1mm" padding-before="20mm"> <xsl:variable name="barcode" select="concat('/prescriptions/', id, '/barcode.html')"/> <fo:external-graphic> <xsl:attribute name="src"> <xsl:value-of select="$barcode" /> </xsl:attribute> </fo:external-graphic> </fo:block>
自定义CustomResourceResolver类
package embedding; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.xmlgraphics.io.Resource; import org.apache.xmlgraphics.io.ResourceResolver; import java.io.BufferedInputStream; import java.net.URI; import java.net.URISyntaxException; public class CustomResourceResolver implements ResourceResolver { String base; String session; String cookie; String user_id; String authorization; static final Logger logger = Logger.getLogger("fop-log"); public String get_base(){ return base; } public CustomResourceResolver(String _base, String _session, String _cookie, String _user_id, String _authorization){ super(); base = _base; session = _session; cookie = _cookie; user_id = _user_id; authorization = _authorization; } @Override public Resource getResource(URI uri) throws IOException { HttpURLConnection urlConnection = null; try { String href = uri.getRawPath(); if (!href.startsWith("/") && !base.endsWith("/")) { base = base + "/"; } String new_uri; if (href.startsWith("http://") || href.startsWith("https://")) { new_uri = href; } else { new_uri = base + href; } uri = new URI(new_uri); logger.log(Level.INFO, "loading: ({0})", new_uri_uri); // 此处存在笔误 URL url = uri.toURL(); urlConnection = (HttpURLConnection) url.openConnection(); if(session!= null){ urlConnection.setRequestProperty("Cookie", cookie + "=" + session); } if(authorization != null){ urlConnection.setRequestProperty("Fop-authorization", authorization ); } if(user_id != null){ urlConnection.setRequestProperty("Fop-user",user_id ); } InputStream in = new BufferedInputStream(urlConnection.getInputStream()); return new Resource(in); } catch (MalformedURLException e) { logger.log(Level.SEVERE, null, e); return null; } catch (IOException | URISyntaxException ioe) { logger.log(Level.SEVERE, null, ioe); return null; } finally { if (urlConnection != null) { urlConnection.disconnect(); } } } @Override public OutputStream getOutputStream(URI uri) throws IOException { URL url = uri.toURL(); return url.openConnection().getOutputStream(); } }
调用代码
CustomResourceResolver resolver = new CustomResourceResolver(base, session, cookie, user_id, authorization); FopFactoryBuilder builder = new FopFactoryBuilder(new File(".").toURI(), (ResourceResolver)resolver); final FopFactory fopFactory = builder.build(); FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); OutputStream out = new java.io.FileOutputStream(pdffile); out = new java.io.BufferedOutputStream(out); try { Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out); TransformerFactory factory = TransformerFactory.newInstance(); URIResolver uiResolver = new CustomUriResolver(base, session, cookie, user_id, authorization); factory.setURIResolver(uiResolver); Transformer transformer = factory.newTransformer(new StreamSource(xsltfile)); // 设置样式表中的<param>参数 transformer.setParameter("versionParam", "2.0"); // 配置XSLT转换输入源 Source src = new StreamSource(xmlfile); // 将生成的FO SAX事件传递给FOP处理 Result res = new SAXResult(fop.getDefaultHandler()); // 启动XSLT转换和FOP处理 transformer.transform(src, res); } finally { out.close(); }
命令行调用方式
java -jar FopXml2Pdf.jar file.xml file.xsl file.config
运行错误日志
Sep 19, 2023 6:52:36 AM embedding.CustomResourceResolver getResource INFO: loading: (http://localhost:3000/prescriptions/420/barcode.html) Sep 19, 2023 6:52:36 AM org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext createImageSource SEVERE: Unable to create ImageInputStream for InputStream from system identifier 'file:/media/sf_Clintel/careright/./' (stream is closed) Sep 19, 2023 6:52:36 AM org.apache.fop.events.LoggingEventListener processEvent SEVERE: Image not found. URI: /prescriptions/420/barcode.html. (No context info available)
从日志可见,已正确加载目标URL并获取数据,但new File(".").toURI()解析为本地路径,导致图片加载失败,请问该如何修改?
解决方案
1. 修正FopFactoryBuilder的基础URI
无需使用本地路径作为基础URI,直接传入你的服务基础URI(如http://localhost:3000/),这样FOP会以该URI为基准处理资源路径。修改调用代码:
// 替换原new File(".").toURI(),使用传入的base作为基础URI FopFactoryBuilder builder = new FopFactoryBuilder(URI.create(base), resolver);
2. 修复CustomResourceResolver中的笔误
代码中new_uri_uri是错误变量名,改为new_uri:
logger.log(Level.INFO, "loading: ({0})", new_uri);
3. 避免输入流过早关闭
原代码在finally块中直接断开urlConnection,会导致返回的InputStream被提前关闭。自定义一个流包装类,在流实际关闭时再断开连接:
// 在CustomResourceResolver类内部添加该包装类 private class CloseTrackingInputStream extends BufferedInputStream { private HttpURLConnection connection; public CloseTrackingInputStream(InputStream in, HttpURLConnection connection) { super(in); this.connection = connection; } @Override public void close() throws IOException { try { super.close(); } finally { connection.disconnect(); } } }
4. 关联资源的正确URI
返回Resource时传入目标URI,让FOP能正确识别资源标识:
InputStream in = new CloseTrackingInputStream(urlConnection.getInputStream(), urlConnection); return new Resource(in, targetUri); // targetUri是拼接后的完整URI
最终修改后的getResource方法片段
@Override public Resource getResource(URI uri) throws IOException { HttpURLConnection urlConnection = null; try { String href = uri.toString(); // 改用toString避免getRawPath丢失路径信息 String new_uri; if (href.startsWith("http://") || href.startsWith("https://")) { new_uri = href; } else { if (!href.startsWith("/") && !base.endsWith("/")) { base = base + "/"; } new_uri = base + href; } URI targetUri = new URI(new_uri); logger.log(Level.INFO, "loading: ({0})", new_uri); URL url = targetUri.toURL(); urlConnection = (HttpURLConnection) url.openConnection(); if(session!= null){ urlConnection.setRequestProperty("Cookie", cookie + "=" + session); } if(authorization != null){ urlConnection.setRequestProperty("Fop-authorization", authorization ); } if(user_id != null){ urlConnection.setRequestProperty("Fop-user",user_id ); } InputStream in = new CloseTrackingInputStream(urlConnection.getInputStream(), urlConnection); return new Resource(in, targetUri); } catch (MalformedURLException | URISyntaxException e) { logger.log(Level.SEVERE, null, e); throw new IOException("Failed to resolve resource", e); } }
内容的提问来源于stack exchange,提问作者Ben Price
相关产品推荐
相关产品推荐

