如何重新创建仅修改文件名的链式 OutputStream
How can I recreate a chained OutputStream with only filename modified
我有一个 OutputStream,它可以被初始化为一个 OutputStreams 链。可以有任何级别的链接。唯一可以保证的是链的末尾是 FileOutputStream。
我需要在 FileOutputStream 中使用修改后的文件名重新创建这个链式输出流。如果可以访问 out
变量(存储底层链式输出流),这是可能的;如下图
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);
}
}
还有其他方法可以达到同样的效果吗?
您可以使用反射来访问 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...
}
}
虽然这在您当前的应用程序中可能没问题,但在生产环境中这是一个很大的问题 no-no,因为您的应用程序可能会收到大量不同类型的 OutputStreams。
更好的解决方法是使用一种 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
进行分片,根据具有同步写入模式的 1024 缓冲区进行 gzip。
我有一个 OutputStream,它可以被初始化为一个 OutputStreams 链。可以有任何级别的链接。唯一可以保证的是链的末尾是 FileOutputStream。
我需要在 FileOutputStream 中使用修改后的文件名重新创建这个链式输出流。如果可以访问 out
变量(存储底层链式输出流),这是可能的;如下图
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);
}
}
还有其他方法可以达到同样的效果吗?
您可以使用反射来访问 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...
}
}
虽然这在您当前的应用程序中可能没问题,但在生产环境中这是一个很大的问题 no-no,因为您的应用程序可能会收到大量不同类型的 OutputStreams。
更好的解决方法是使用一种 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
进行分片,根据具有同步写入模式的 1024 缓冲区进行 gzip。