有没有办法在不关闭底层流的情况下关闭 Writer?

Posted

技术标签:

【中文标题】有没有办法在不关闭底层流的情况下关闭 Writer?【英文标题】:Is there a way to close a Writer without closing the underlying stream? 【发布时间】:2013-02-18 01:05:09 【问题描述】:

我有一个套接字,我可以在其中写入一些字符数据和一些原始字节数据。对于字符数据,使用PrintWriter 更容易。对于原始字节数据,直接写入OutputStream 更容易。所以在我的代码中,我有这样的片段:

Writer writer = new PrintWriter(outputStream);
writer.write(someText);
...
writer.flush();
// No call to writer.close(), because that would close the underlying stream.

只要我在开始以其他方式写入流后小心不要写入此Writer,就可以了。但如果我不小心写入流(就像我关闭它一样),我更愿意知道我会得到IOException 的安全性。

有没有办法在不关闭其基础流的情况下明确阻止将来写入 Writer

【问题讨论】:

【参考方案1】:

简单地说,不。 Java io 流类的编写方式,它们总是链接关闭操作。当然,您可以创建自己的编写器实现来覆盖此行为。

【讨论】:

【参考方案2】:

为什么? close() 只做两件事:(1)刷新写入器和(2)在嵌套写入器上调用close()。如果您不想(2),请自己致电flush(),并且根本不要致电close()

【讨论】:

【参考方案3】:

我会创建一个允许写入字符和二进制数据的特殊类,例如:

class CombinedWriter extends Writer 
    private boolean isWritingBinary;
    private Writer mWriter;
    private OutputStream mOutputStream;
    public void write(byte[] bytes) 
        // flush the writer if necessary
        isWritingBinary = true;
        mOutputStream.write(bytes);
    
    public void write(String string) 
        // flush if necessary
        isWritingBinary = false;
        mWriter.write(string);
    
    public void flush() 
        // ...
    
    public void close() 
        // ...
    

它可以扩展也可以不扩展Writer;在后一种情况下,您不需要覆盖代码中未使用的方法。

编写器刷新的技巧仍然存在,但它被本地化为一个类;此外,如果该技巧在未来的某个版本中出现问题,则可能会重写该类以消除该技巧(可能需要从 android 源代码中借用一部分代码)。

【讨论】:

【参考方案4】:

这是对字符和二进制数据都使用 OutputStream 的方法:

byte strBin[] = someText.getBytes("UTF-8");
outputStream.write(strBin);

如果您想要different 编码,请替换“UTF-8”。

【讨论】:

最好使用 java.nio.charset 包中的 StandardCharsets.UTF_8,而不是在 java 7+ 上使用字符串文字对名称进行硬编码【参考方案5】:

感谢您的提问!我遇到了完全相同的问题,而且很容易解决。从阅读您的问题来看,您可能正在尝试做我想做的事情,所以我想通过提供对我有用的解决方案来提供帮助。

在我的例子中,我试图为 jpg 编写 HTTP 响应,在这种情况下,文本标题部分将跟随二进制数据。这里的流是Java SocketOutputStream

我使用PrintWriter 将文本写入套接字流,但随后我需要写入二进制数据。有一个简单的解决方案,它适用于二进制数据跟随文本数据的情况,并且二进制数据之后没有更多的文本数据。

只需在流上打开一个Printwriter 并使用编写器将文本打印到流中,直到所有文本都被写入。然后将PrintWriter 刷新到流中,但不要关闭它(这会关闭必须保持打开的基础流)。最后,将二进制数据直接写入流中。

最后,您可以简单地关闭 PrintWriter 以关闭底层流。

如果使用下面提供的类,你会:

    通过为基础Socket 提供OutputStream 来构造HttpStreamWriterImpl。 根据需要反复拨打writeLine()。 如有需要,请致电writeBinary()。 完成后致电close()

例子:

public class HttpStreamWriterImpl implements Closeable

    private @NotNull OutputStream stream;
    private @NotNull PrintWriter printWriter;

    public HttpStreamWriterImpl(@NotNull OutputStream stream)
    
        this.stream = stream;
        this.printWriter = new PrintWriter(stream, true, UTF_8);
    

    public void writeLine(@NotNull String line)
    
        printWriter.print(line);
        printWriter.print(HTTP_LINE_ENDING);
    

    public void writeBinary(@NotNull byte[] binaryContent) throws IOException
    
        printWriter.flush();
        stream.write(binaryContent);
        stream.flush();
    

    @Override
    public void close()
    
        printWriter.close();
    

【讨论】:

以上是关于有没有办法在不关闭底层流的情况下关闭 Writer?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不失去对 React Native 的关注的情况下关闭键盘。(至少显示光标)

有啥方法可以在不关闭其 BaseStream 的情况下关闭 StreamWriter?

在不添加完成键的情况下关闭数字键盘式键盘

如何在不关闭模式的情况下关闭从 modalViewController 推送的 viewController?

iOS:在不访问其父 ViewController 的情况下关闭和呈现 ModalViewController

如何在不关闭 TCP 连接的情况下关闭处理 TCP 请求的线程?