Freemarker中的管道流问题

时间:2010-09-02 07:12:51

标签: java blocking

我需要在freemarker中加载和处理模板。我正在使用管道流来回读freemarker生成的结果。

示例代码:

PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);
Writer writer = new OutputStreamWriter(po);
configuration.getTemplate("xx").process(rootMap, writer);

问题在于,有时它会在freemarker procsss方法中冻结。 没有错误,没有异常,但它没有从process方法返回。

如果我将管道流转换为ByteArray流,它可以正常工作。

我是否以正确的方式使用管道流?

3 个答案:

答案 0 :(得分:3)

不,管道流旨在在两个线程之间传递数据。管道两端之间只有一个小缓冲区。如果您写入管道输出流,如果缓冲区已满,您的线程将被阻止,直到另一个线程将从相应的管道输入流中读取。这不适用于一个线程。

来自Javadoc:

  

通常,从a读取数据   PipedInputStream一个对象   线程和数据写入   相应的PipedOutputStream by   其他一些帖子。

因此,对于小型模板,只需使用StringWriter,对于大型模板,您可以在FileWriter创建的临时文件上使用File.createTempFile()

答案 1 :(得分:1)

正如Arne所写,管道流中的缓冲区空间量相当小。如果您不能使用可以容纳所有数据的缓冲区(无论是在内存中还是在磁盘上),那么您可以尝试的一件事就是看看是否可以在另一个线程中运行模板处理,并使用管道流将结果发回到你正在做的主线程。

PipedInputStream pi = new PipedInputStream();
final Writer writer = new OutputStreamWriter(new PipedOutputStream(pi));
Thread worker = new Thread(new Runnable() {
    public void run() {
        configuration.getTemplate("xx").process(rootMap, writer);
    }
});
worker.start();

您可能需要将final个关键字添加到其他变量中,以使其在您的实际代码中运行。这取决于变量configurationgetTemplaterootMap变量的参数是局部变量还是实例(或类)变量。

(当然,在指定线程的行为时,我可以将Thread子类化,但我更喜欢在这种情况下实例化一个接口 - Runnable - 对于这样的事情。)

答案 2 :(得分:1)

这就是我的工作方式。

final String uploadReportAsCsv = FreeMarkerTemplateUtils.processTemplateIntoString(
                        fileUploadReportTemplate, modelMap);

message.addAttachment("fileUploadProcessedReport.csv",
    new InputStreamSource() {
        //InputStreamResource from Spring is always returning an open stream,
        // thus we need to create this anonymous class.
        @Override
        public InputStream getInputStream() throws IOException {
            return new StringInputStream(uploadReportAsCsv);
        }
    }
);