如何在 Spring MVC Web 应用程序中将 Apache FOP 与 Velocity 结合使用?

发布于 2024-12-04 08:57:08 字数 1643 浏览 2 评论 0原文

我在 Spring 上下文中对 Velocity 进行了简单的配置(根据官方 Spring 文档)并且工作正常。如何配置/将其与 Apache FOP 集成并生成 pdf 文档?如果有一些例子,我将不胜感激。

<!-- velocity -->
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
</bean>
<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    <property name="cache" value="true"/>
    <property name="prefix" value=""/>
    <property name="suffix" value=".vm"/>
</bean>

测试控制器:

@Controller
@RequestMapping("/doc")
public class DocumentController {

    @RequestMapping("/test")
    public ModelAndView velocityTest() {
        List<String> xmens = new ArrayList<String>();
        xmens.add("Professor X");
        xmens.add("Cyclops");
        xmens.add("Iceman");
        xmens.add("Archangel");
        xmens.add("Beast");
        xmens.add("Phoenix");
        Map<String, List<String>> model = new HashMap<String, List<String>>();
        model.put("xmens", xmens);
        return new ModelAndView("testdoc", model);      
    }
}

/WEB-INF/velocity/test.vm

<html>
    <body>
        <ul>
        #foreach ($xmen in $xmens)
            <li>$xmen</li>
        #end
        </ul>
    </body>
</html>

I have simple configuration of Velocity in Spring context (according to an official Spring documentation) and works ok. How to configure/integrate this with Apache FOP and generate pdf documents ? I would be grateful for some examples.

<!-- velocity -->
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
</bean>
<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    <property name="cache" value="true"/>
    <property name="prefix" value=""/>
    <property name="suffix" value=".vm"/>
</bean>

Test controller:

@Controller
@RequestMapping("/doc")
public class DocumentController {

    @RequestMapping("/test")
    public ModelAndView velocityTest() {
        List<String> xmens = new ArrayList<String>();
        xmens.add("Professor X");
        xmens.add("Cyclops");
        xmens.add("Iceman");
        xmens.add("Archangel");
        xmens.add("Beast");
        xmens.add("Phoenix");
        Map<String, List<String>> model = new HashMap<String, List<String>>();
        model.put("xmens", xmens);
        return new ModelAndView("testdoc", model);      
    }
}

/WEB-INF/velocity/test.vm

<html>
    <body>
        <ul>
        #foreach ($xmen in $xmens)
            <li>$xmen</li>
        #end
        </ul>
    </body>
</html>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

忱杏 2024-12-11 08:57:08

我这样做了,但我认为这肯定是一个更优雅的解决方案(testpdf.vmtestpdf.xsl位于/WEB-INF/velocity< /代码>)。

@Controller
@RequestMapping("/doc")
public class DocumentController {

    @Autowired
    private PdfReportService pdfReportService;

    @RequestMapping("/pdf")
    public void testPdf(HttpServletResponse response) throws IOException {
        Map<String, Object> model = new HashMap<String,Object>(); 
        model.put("message", "Hello World!");
        pdfReportService.generatePdf("testpdf", model, response.getOutputStream());
    }

}

pdf报告服务:

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;

@Service("pdfReportService")
public class PdfReportService {

    private final Logger log = Logger.getLogger(getClass());

    @Autowired
    private VelocityConfigurer velocityConfig;

    @Autowired
    private ServletContext servletContext; 

    public void generatePdf(String templateName, Map<String,Object>model, OutputStream out) {

        // get an engine
        final VelocityEngine engine = velocityConfig.getVelocityEngine();

        // get the Template
        Template template = engine.getTemplate(templateName+".vm");

        // create a context and add data
        VelocityContext context = new VelocityContext();
        context.put("model", model);

        // render the template into a StringWriter
        StringWriter writer = new StringWriter();
        template.merge(context, writer);

        FopFactory fopFactory = FopFactory.newInstance();

        try {
            //Setup FOP
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);

            //Setup Transformer
            InputStream xstlIn = servletContext.getResourceAsStream("/WEB-INF/velocity/"+templateName+".xsl");
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer(new StreamSource(xstlIn));

            //Make sure the XSL transformation's result is piped through to FOP
            Result res = new SAXResult(fop.getDefaultHandler());

            //Setup input
            byte[] bytes = writer.toString().getBytes("UTF-8");
            Source src = new StreamSource(new ByteArrayInputStream(bytes));

            //Start XSLT transformation and FOP processing
            transformer.transform(src, res);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}

I did it this way, but I think it is certainly a more elegant solution (testpdf.vm and testpdf.xsl are in /WEB-INF/velocity).

@Controller
@RequestMapping("/doc")
public class DocumentController {

    @Autowired
    private PdfReportService pdfReportService;

    @RequestMapping("/pdf")
    public void testPdf(HttpServletResponse response) throws IOException {
        Map<String, Object> model = new HashMap<String,Object>(); 
        model.put("message", "Hello World!");
        pdfReportService.generatePdf("testpdf", model, response.getOutputStream());
    }

}

PdfReportService:

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;

@Service("pdfReportService")
public class PdfReportService {

    private final Logger log = Logger.getLogger(getClass());

    @Autowired
    private VelocityConfigurer velocityConfig;

    @Autowired
    private ServletContext servletContext; 

    public void generatePdf(String templateName, Map<String,Object>model, OutputStream out) {

        // get an engine
        final VelocityEngine engine = velocityConfig.getVelocityEngine();

        // get the Template
        Template template = engine.getTemplate(templateName+".vm");

        // create a context and add data
        VelocityContext context = new VelocityContext();
        context.put("model", model);

        // render the template into a StringWriter
        StringWriter writer = new StringWriter();
        template.merge(context, writer);

        FopFactory fopFactory = FopFactory.newInstance();

        try {
            //Setup FOP
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);

            //Setup Transformer
            InputStream xstlIn = servletContext.getResourceAsStream("/WEB-INF/velocity/"+templateName+".xsl");
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer(new StreamSource(xstlIn));

            //Make sure the XSL transformation's result is piped through to FOP
            Result res = new SAXResult(fop.getDefaultHandler());

            //Setup input
            byte[] bytes = writer.toString().getBytes("UTF-8");
            Source src = new StreamSource(new ByteArrayInputStream(bytes));

            //Start XSLT transformation and FOP processing
            transformer.transform(src, res);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}
云裳 2024-12-11 08:57:08

我完全按照 marioosh 的方式做的。后来,当我们有很多用户同时进行 PDF 对话时,我们在大型 Web 应用程序中遇到了问题(内存不足),因为 VelocityEngine 和 Apache FOP 都需要一些(大量)内存,并且当您有许多并发用户时,这总结起来。

我们改变了流媒体的方式。 Velocity 现在将 XSL-FO 流式传输到 Apache FOP。 FOP 将结果流式传输到客户端。我们使用 PipedReader / PipedWriter。请注意,此解决方案需要额外的线程。我无法分享此代码,因为我们为我们的客户这样做了。

同时,我找到了一个用于在网络上进行流传输的现有解决方案,请参阅 存档。但请注意,此解决方案通过 new Thread(worker).start(); 创建一个额外的线程
在应用程序服务器中,您应该使用 改为 WorkManager。另请参阅 http://www.devx.com/java/Article/28815/1954< /a>

I did it exactly as marioosh. Later on we had issues (OutOfMemory ecxeptions) in a big web application when we had a lot of users doing PDF conversation at the same time since both the VelocityEngine and Apache FOP need some (a lot of) memory and when you have many concurrent users, this sums up.

We changed the approach to streaming. Velocity streams the XSL-FO to Apache FOP now. FOP streams the result to the client. We did this with PipedReader / PipedWriter. Please note that this solution needs an extra thread. I cannot share this code as we did it for a customer of us.

Meantime I found an existing solution for streaming on the web, see the archive. But note that this solution creates an extra thread via new Thread(worker).start();
In an application server, you should rather use a WorkManager instead. See also http://www.devx.com/java/Article/28815/1954

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文