如何在Spring MVC web-app中使用带有Velocity的Apache FOP?

时间:2011-09-14 07:00:14

标签: spring-mvc velocity apache-fop

我在Spring上下文中有简单的Velocity配置(根据官方Spring documentation)并且正常工作。如何使用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>

2 个答案:

答案 0 :(得分:6)

我是这样做的,但我认为这肯定是一个更优雅的解决方案(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());
    }

}

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);
        }
    }

}

答案 1 :(得分:2)

我完全像marioosh那样做了。后来我们在一个大型Web应用程序中遇到了问题(OutOfMemory ecxeptions)当我们有很多用户同时进行PDF对话时,因为VelocityEngine和Apache FOP都需要一些(很多)内存以及当你有很多并发用户时这总结了。

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

与此同时,我找到了一个现有的网络流媒体解决方案,请参阅http://www.ibstaff.net/fmartinez/?p=15 但请注意,此解决方案通过new Thread(worker).start();创建了一个额外的线程 在应用程序服务器中,您应该使用WorkManager代替。另请参阅http://www.devx.com/java/Article/28815/1954