org.apache.fop.apps.FOPException: .fop (访问被拒绝) 在 Azure 中

3

我最近在现有应用程序中添加了一个fop pdf生成器。在本地一切正常,但当我部署到Azure上时,出现以下错误:

org.apache.fop.apps.FOPException: .fop (Access is denied)
java.io.FileNotFoundException: .fop (Access is denied)

我感到困惑的是,当fopfactory写入ByteOutputstream时似乎发生了异常,但我却收到文件访问错误。

捕获异常的函数是:

        public static void GeneratePDF(string foFile, HttpContext context) {
        StringBuilder sbDebug = new StringBuilder();

        sbDebug.AppendLine("Start");
        ByteArrayOutputStream os = new java.io.ByteArrayOutputStream();
        try
        {
            sbDebug.AppendLine("Got to 1");
            com.sun.org.apache.xerces.@internal.jaxp.SAXParserFactoryImpl s = new com.sun.org.apache.xerces.@internal.jaxp.SAXParserFactoryImpl();
            sbDebug.AppendLine("Got to 2");
            FopFactory fopFactory = FopFactory.newInstance(new java.io.File(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/Test.xconf")));
            sbDebug.AppendLine("Got to 3 - file ").AppendLine(System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/Test.xconf"));
            Fop fop = fopFactory.newFop("application/pdf", os);

            sbDebug.AppendLine("Got to 4");
            javax.xml.transform.TransformerFactory factory = javax.xml.transform.TransformerFactory.newInstance();
            javax.xml.transform.Transformer transformer = factory.newTransformer();
            javax.xml.transform.Source src = new javax.xml.transform.stream.StreamSource(new java.io.StringReader(foFile));
            javax.xml.transform.Result res = new javax.xml.transform.sax.SAXResult(fop.getDefaultHandler());
            sbDebug.AppendLine("Got to 5");
            transformer.transform(src, res);

            context.Response.ContentType = "application/pdf";
            context.Response.BinaryWrite(os.toByteArray());
            //context.Response.BinaryWrite(System.Text.Encoding.UTF8.GetBytes(os.ToString()));
        }

        catch (Exception ex) {
            throw new Exception(sbDebug.AppendLine(ex.ToString()).ToString());
        }

        finally {
            os.close();
        }
    }

你可以看到,我添加了一些调试代码,因为我必须采用老式的调试方式来查看发生了什么。 这是输出结果:

Catch Error: System.Exception: Start
Got to 1
Got to 2
Got to 3 - file 
D:\home\site\wwwroot\App_Data\Test.xconf
org.apache.fop.apps.FOPException: .fop (Access is denied)
java.io.FileNotFoundException: .fop (Access is denied)

所以我可以看到异常发生在这一行:

Fop fop = fopFactory.newFop("application/pdf", os);

我希望除了读取xconf文件的部分,所有操作都在内存中进行。 更新 21/02/2017 我已经验证xconf文件存在于正确的位置。
string curFile = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/Test.xconf");
            if (System.IO.File.Exists(curFile)) //Used System.IO prefix as without it compiler complains of ambiguity with java.io
            {
                sbDebug.AppendLine("Got to 3a - file found");
            }
            else
            {
                sbDebug.AppendLine("Got to 3a - file NOT found");
            }

同时,它也可以使用以下方式进行阅读。
BufferedReader br = new BufferedReader(new FileReader(curFile));
            sbDebug.AppendLine("Got to 3b");

            String sJIObrOut = br.readLine();
            sbDebug.AppendLine("Got to 3c");
            while ((sJIObrOut = br.readLine()) != null) 
            {
                sbDebug.Append("3d: ").AppendLine(sJIObrOut);
            }

这表明fopfactory试图访问不同的文件,但我无法找出是哪个文件。
非常感谢任何帮助。
更新2 22/3/17 我在Azure云服务上没有成功,所以决定构建一个本地Web应用程序,仅将xslfo文档转换为pdf并将其作为Web服务运行。 这在本地完美运行,因此将该服务发布到我们为其他客户使用的服务器上。 这不起作用,我得到与原始帖子完全相同的错误。 这意味着不是Azure的问题 - 是否有任何其他想法?

2
在我的Linux系统上,.fop用于存储缓存文件,如字体缓存。它位于/home/USER/.fop下,其中USER是tomcat运行的用户。因此,我推测FOP希望在Windows的C:\Users\USER\AppData\Local某处创建此文件,但这可能在Azure上不被允许。我是一个Linux人,所以我不确定,但也许这会有所帮助。 - mondjunge
5个回答

3
非常感谢@mondjunge指导我朝着正确的方向前进。目前我有一个在Azure之外工作的解决方案 - 随着时间的推移,我希望这将是我可以直接应用于Azure的东西,并且我将相应地添加到这篇文章中。
看起来缓存写入是导致问题(权限)的原因。
我实施的解决方法/修复方法是将以下行添加到xconf文件中(我将它放在/fop关闭标记之前,但认为只要它是fop的直接子节点,就不重要)。
<use-cache>false</use-cache>

希望这篇文章能够为其他人节省数天的麻烦和压力。

更新于 2017 年 7 月 4 日

出于兴趣,我尝试将这行代码添加到纯 Azure 托管的版本中,结果不再显示权限错误,而是显示了关于 GDI 的错误,这可能与 @PeterPan 最初提出的建议类似。由于我在其他地方托管了一个工作版本的 fop 应用程序,所以我决定就此结束。


2
ApacheFOP在第一次运行时会在本地创建.Fop文件夹,并在其中创建cache-fonts.cache文件(如果您想定义一些其他不受Apache FOP支持的字体,这个缓存文件可以提高性能,以及许多其他功能)。 如果您进入Windows用户,您会发现这个.fop文件夹。 无论如何,我认为Azure不允许Apache FOP在服务器上创建这个.fop文件夹并在其中创建fop-cache.cache文件。
我花了一些时间来解决这个问题! 以下是我的问题的解决方案(它适用于Windows Server): 步骤: 1- 您定义fop-fonts.cache路径 2- 您必须调整缓存文件的路径 3- 设置缓存路径,fop-fonts.cache将在其中创建
代码:
    // create just Fop folder and inside this a Cache FOP Folder on your 
    // project
    // Tipp: you could read it from config
    // fop-fonts.cache have to be created automaticly, if it is running good
    string fopcacheFilePath = "~/Fop/Cache Fop/fop-fonts.cache"; 

    fopcacheFilePath = fopcacheFilePath.Replace("\\","/");
    fopcacheFilePath = "file:///" + fopcacheFilePath;
    fopcacheFilePath = fopcacheFilePath.Replace(" ", "%20");
    //in your case you have to set fopcacheFilePath
    fopFactory.getFontManager().setCacheFile(new URI(fopcacheFilePath))

如果这段代码一直没有问题运行,那么你需要在项目文件夹中找到fop-fonts.cache文件。

谢谢@Moe - 我现在打算放下这个问题,因为我已经有需要的字体和一个可行的解决方案了。如果需要其他字体,我可能会尝试你的建议。 - RT72
也可以将其设置在fop-config.xml文件中:<cache-file>file:///tmp/fop-fonts.cache</cache-file> - Sergio Gabari

0

我解决了给予IIS用户修改路径的访问权限问题:(Windows服务器)

C:\Windows\system32\config\systemprofile

我的本地机器的主目录路径是 User\My User,但在服务器上,该路径是在 system32 文件夹中的路径。
string home = java.lang.System.getProperty("user.home"); // return the home path

希望这能对你有所帮助。


0

我研究了Apache FOP,发现其生成PDF的功能基于Java AWT,在Windows操作系统中使用默认的DirectDraw/GDI管道。然而,根据kudu wiki页面Azure Web App sandbox,您可以通过搜索关键字GDI获取有关GDI的限制信息。因此,通常的解决方法是使用一个不带GDI的库,但似乎几乎所有用于图像处理的Java库都依赖于AWT。

除了使用Azure云服务或VM外,在Azure应用程序服务(包括WebApps)上无法生成PDF。


我现在使用的fop pdfgenrator(crispin.fop)是fonet的替代品,后者是apache FOP的一个非常古老的.NET端口,并因缺乏对许多xsl:fo功能的支持而被替换。这个解决方案在Azure上生成PDF文件很好用 - 你有任何想法为什么那个可以工作而这个不行吗? - RT72
在Linux上的App Service上运行怎么样?我知道它还在预览阶段,但也许这是未来可行的方法? - Lech Migdal
看了我发的原始故障,我并不确定我们是否已经达到涉及GDI的阶段。虽然我离这方面的专家还有很远的路要走,但我认为只有在启动/调用转换器之后才会发生GDI相关的事情,而这是在异常被抛出之后发生的 - 你同意吗? - RT72
请查看我的更新,日期为22/3/17 - Azure不是问题 - 请问还有其他的想法吗? - RT72

0

它起作用了!

对于典型的Java Spring Tomcat应用程序,配置文件应该位于WEB-INF下。

或者

从设置输入流以与配置对象一起使用的源代码中找到它。

--供您参考的示例代码

Resource config = m_appContext.getResource ("/WEB-INF/user_config.xml");
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder (true);
m_config = builder.build (config.getInputStream ());
FopFactory fopFactory = FopFactory.newInstance();
if (null != m_config)
     fopFactory.setUserConfig (m_config);

回复:对于<use-cache>false</use-cache> - kaushal patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接