【问题标题】:Is there a way to close a Writer without closing the underlying stream?有没有办法在不关闭底层流的情况下关闭 Writer?
【发布时间】: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?

【问题讨论】:

    标签: java sockets io outputstream


    【解决方案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 Socket 的OutputStream。

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

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

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

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

          1. 通过为基础Socket 提供OutputStream 来构造HttpStreamWriterImpl。
          2. 根据需要反复拨打writeLine()。
          3. 如有需要,请致电writeBinary()。
          4. 完成后致电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();
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2011-05-20
            • 1970-01-01
            • 1970-01-01
            • 2019-11-04
            • 1970-01-01
            • 1970-01-01
            • 2015-09-20
            • 2015-08-11
            • 2019-12-18
            相关资源
            最近更新 更多