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

org.apache.fop.apps.FOPException: .fop (Access is denied) in Azure

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

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

我很困惑为什么当 fopfactory 写入 ByteOutputstream 时出现异常时我会收到文件访问错误

发生catch的函数是

        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 文件之外,我希望所有事情都发生在内存中。

2017 年 2 月 21 日更新 我现在已经使用

验证了 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 服务。这在本地完美运行,因此在我们用于其他客户端的服务器上发布了服务。 这不起作用,我得到的错误与我原来的 post 完全相同。这意味着这不是 Azure 的问题 - 有人有任何其他想法吗?

我研究了Apache FOP,我发现PDF生成功能是基于Java AWT which using Default DirectDraw/GDI Pipeline on Windows OS. However, according to the kudu wiki page Azure Web App sandbox,你可以通过搜索关键字GDI获得GDI的限制信息。所以通常解决方法是使用没有 GDI 的库,但似乎几乎所有 Java 图像库都依赖于 AWT。

除了使用 Azure 云服务或 VM,您无法在包含 WebApps 的 Azure 应用服务上生成 PDF。

非常感谢@mondjunge 为我指明了正确的方向。我现在有一个在 Azure 之外工作的解决方案 - 在适当的时候我希望这将是我可以直接应用于 Azure 的东西,我将相应地添加到这个 post

似乎缓存写入是导致问题的原因(权限)。

我实现的workaround/fix是在xconf文件中加入下面这行(我把它放在/fop结束标签之前,但认为它不重要,只要它是一个直接的子节点fop)

<use-cache>false</use-cache>

希望这可以为其他人省去几天的麻烦和压力

2017 年 7 月 4 日更新

出于兴趣,我尝试将此行添加到纯 azure 托管版本,它不再给我权限错误,而是给了我一个关于 GDI 的错误,这可能与@PeterPan 最初建议的一致。由于我有一个在别处托管的 fop 应用程序的工作版本,我将保留它。

ApacheFOP 将首先在本地创建 .Fop(文件夹) 运行 并在此文件夹内创建缓存-fonts.cache 文件(此缓存文件用于提高性能,如果您想定义一些其他字体,Apache FOP 和许多其他东西不支持)。 如果你进入你的 windows 用户,你会发现这个 .fop 文件夹 无论如何,我认为 azure 不允许 apache fop 在服务器上创建这个 .fop 文件夹以在这个 .fop 中创建 fop-cache.cache 文件

我花了一些时间才得到解决方案! 这是我的问题的解决方案(它正在 windows 服务器上工作) 脚步: 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

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

C:\Windows\system32\config\systemprofile

我本地机器的home路径是User\My User,但在服务器中是system32文件夹中的路径。

string home = java.lang.System.getProperty("user.home"); // return the home path

希望对你有所帮助。

成功了!

仅供参考 对于典型的 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);