我有一个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);
}
}
还有其他方法可以达到同样的效果吗?
答案 0 :(得分:1)
您可以使用反射来访问FilterOutputStream
的os.out字段,但这有一些缺点:
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缓冲区。