Как использовать Apache FOP с Velocity в веб-приложении Spring MVC? - PullRequest
1 голос
/ 14 сентября 2011

У меня есть простая конфигурация Velocity в контексте Spring (согласно официальной 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 / скорость / test.vm

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

Ответы [ 2 ]

6 голосов
/ 14 сентября 2011

Я сделал это таким образом, но я думаю, что это, безусловно, более элегантное решение (testpdf.vm и testpdf.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);
        }
    }

}
2 голосов
/ 23 февраля 2012

Я сделал это точно так же, как marioosh. Позже у нас возникли проблемы (исключения OutOfMemory) в большом веб-приложении, когда у нас было много пользователей, одновременно выполняющих 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

...