如何重新创建仅修改了文件名的链式OutputStream

时间:2016-01-13 10:52:56

标签: java outputstream fileoutputstream

我有一个OutputStream,可以初始化为OutputStreams链。可能存在任何级别的链接。只保证在链的末尾是FileOutputStream。

我需要在FileOutputStream中使用修改后的Filename重新创建此链式outputStream。如果out变量(存储底层链接的outputStream)可访问,那么这是可能的。如下图所示。

public OutputStream recreateChainedOutputStream(OutputStream os) throws IOException {
    if(os instanceof FileOutputStream) {
        return new FileOutputStream("somemodified.filename");
    } else if (os instanceof FilterOutputStream) {
        return  recreateChainedOutputStream(os.out);
    }
}

还有其他方法可以达到同样的效果吗?

1 个答案:

答案 0 :(得分:1)

您可以使用反射来访问FilterOutputStream的os.out字段,但这有一些缺点:

  • 如果另一个OutputStream也是一种RolloverOutputStream,你可能很难重建它,
  • 如果其他OutputStream有自定义设置,比如GZip压缩参数,则无法可靠阅读本文
  • 如果有

recreateChainedOutputStream(的快速而肮脏的实现可能是:

private final static Field out;
{
    try {
        out = FilterInputStream.class.getField("out");
        out.setAccessible(true);
    } catch(Exception e) {
        throw new RuntimeException(e);
    }
}

public OutputStream recreateChainedOutputStream(OutputStream out) throws IOException {
    if (out instanceof FilterOutputStream) {
        Class<?> c = ou.getClass();
        COnstructor<?> con = c.getConstructor(OutputStream.class);
        return con.invoke(this.out.get(out));
    } else {
        // Other output streams...
    }
}

虽然这在您当前的应用程序中可能没问题,但在生产环境中这是一个很大的禁忌,因为您的应用程序可能会收到大量不同类型的OutputStream。

更好的解决方法是使用Function<String, OutputStream>作为工厂为命名文件创建OutputStream。通过这种方式,外部api可以控制OutputStream,同时api可以添加多个文件名。这方面的一个例子是:

public class MyApi {
    private final Function<String, OutputStream> fileProvider;
    private OutputStream current;
    public MyApi (Function<String, OutputStream> fileProvider, String defaultFile) {
        this.fileProvider = fileProvider;
        selectNewOutputFile(defaultFile);
    }
    public void selectNewOutputFile(String name) {
        OutputStream current = this.current;
        this.current = fileProvider.apply(name);
        if(current != null) current.close();
    }
}

然后可以在其他应用程序中使用:

MyApi api = new MyApi(name->new FileOutputStream(name));

对于简单的FileOutputStream,或者用作:

MyApi api = new MyApi(name->
    new GZIPOutputStream(
        new CipherOutputStream(
            new CheckedOutputStream(
                new FileOutputStream(name),
                new CRC32()), 
            chipper),
       1024, 
       true)
   );

对于使用new CRC32()存储校验和的文件流,使用chipper进行削片,gzip根据具有同步写入模式的1024缓冲区。