Java资源目录中XSLT文件引用路径错误,如何正确配置?
问题
我在Java资源目录中存放了example.xsl文件,通过class.getClassLoader().getResourceAsStream("example.xsl")获取该文件的输入流。但example.xsl内部包含引用语句<xsl:include href="dir/i18n.inc.xslt"/>,存放i18n.inc.xslt的dir目录也在资源目录中,部署后程序却在glassfish/domains/domain1/config目录下查找该文件。
使用环境:Java 17、Payara 6,通过Apache FOP实现XML转PDF,相关代码如下:
InputStream styleSheetInput = MyClass.class.getClassLoader().getResourceAsStream("example.xsl"); final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); try { Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outputStream); // Setup XSLT TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource(styleSheetInput)); // Set the value of a <param> in the stylesheet transformer.setParameter("versionParam", "2.0"); // Setup input for XSLT transformation Source src = new StreamSource(datafile); // Resulting SAX events (the generated FO) must be piped through to FOP Result res = new SAXResult(fop.getDefaultHandler()); // Start XSLT transformation and FOP processing transformer.transform(src, res); }
报错信息:
[tid: _ThreadID=279 _ThreadName=__ejb-thread-pool11] [timeMillis: 1693598088423] [levelVal /usr/local/payara6/glassfish/domains/domain1/config/dir/i18n.inc.xslt (No such file or directory)]]
希望将XSLT文件放在资源目录中而非config目录,如何修改让程序在资源目录中查找该文件?
解决方案
问题根源在于:仅用InputStream创建StreamSource时,Transformer没有获取到XSL文件的基础URI,解析相对路径时会默认使用当前工作目录(Payara的config目录)。解决核心是给StreamSource设置正确的系统ID,让Transformer基于资源的实际位置解析相对引用。
方法一:通过资源URL设置系统ID
先获取example.xsl的资源URL,再基于此URL设置StreamSource的系统ID,代码修改如下:
// 获取资源URL,而非直接拿InputStream URL styleSheetUrl = MyClass.class.getClassLoader().getResource("example.xsl"); InputStream styleSheetInput = styleSheetUrl.openStream(); // 创建StreamSource并设置系统ID(资源的完整URI) StreamSource styleSheetSource = new StreamSource(styleSheetInput); styleSheetSource.setSystemId(styleSheetUrl.toString()); // 后续用styleSheetSource创建Transformer Transformer transformer = factory.newTransformer(styleSheetSource);
方法二:直接使用资源URL创建StreamSource
可以省略InputStream,直接用资源URL的字符串形式创建StreamSource,Transformer会自动识别基础URI:
URL styleSheetUrl = MyClass.class.getClassLoader().getResource("example.xsl"); StreamSource styleSheetSource = new StreamSource(styleSheetUrl.toExternalForm()); Transformer transformer = factory.newTransformer(styleSheetSource);
说明
设置系统ID后,XSL中的<xsl:include href="dir/i18n.inc.xslt"/>会基于example.xsl的资源位置(即资源目录)解析相对路径,从而正确找到dir/i18n.inc.xslt文件,无需修改XSL中的href路径。
内容的提问来源于stack exchange,提问作者liubitel

