Is there a way to close a Writer without closing the underlying stream?
Asked Answered
O

5

7

I have a socket to which I write some character data, and some raw byte data. For the character data, it's easier to use a PrintWriter. For the raw byte data, it's easier to write directly to the OutputStream. So throughout my code, I have segments like this:

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

As long as I am careful not to write to this Writer after beginning to write to the stream in some other way, this is fine. But I would prefer the safety of knowing that I'll get an IOException if I accidentally do write to the stream (as I would if I had closed it).

Is there a way to explicitly prevent future writes to a Writer without closing its underlying stream?

Overwrite answered 4/3, 2013 at 1:30 Comment(0)
T
8

Simply put, no. The way Java io stream classes are written, they always chain close operations. You could of course, create your own writer implementation that overrode this behavior.

Tiphanie answered 4/3, 2013 at 1:37 Comment(0)
N
12

Why? close() only does two things: (1) flush the writer and (2) call close() on the nested writer. If you don't want (2), call flush() yourself and don't call close() at all.

Natale answered 4/3, 2013 at 8:38 Comment(0)
T
8

Simply put, no. The way Java io stream classes are written, they always chain close operations. You could of course, create your own writer implementation that overrode this behavior.

Tiphanie answered 4/3, 2013 at 1:37 Comment(0)
C
0

I would create a special class allowing to write both characters and binary data, something like:

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() {
        // ...
    }
}

It may extend or not extend Writer; in the latter case, you do not need to override methods not used in your code.

The trick with the writer flush is still there, but it is localized to one class; in addition, if the trick breaks in some future version, the class may be rewritten to eliminate the trick (one will likely need to borrow a portion of code from Android sources).

Capybara answered 24/2, 2015 at 13:10 Comment(0)
D
0

This is how to use OutputStream for both character and binary data:

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

Replace "UTF-8" if you want a different encoding.

Distant answered 25/4, 2017 at 21:15 Comment(1)
It's better to use StandardCharsets.UTF_8 from java.nio.charset package rather than hard coding the name using string literal on java 7+Suziesuzuki
F
0

Thanks for asking! I had exactly the same problem, and it was easy to solve. From reading your question it sounds like you might be trying to do exactly what I was trying to do, so I wanted to help out by giving the solution that worked for me.

In my case I was trying to write a HTTP response for a jpg, in which case the text header section will be followed by binary data. The stream here is the OutputStream of a Java Socket.

I was using a PrintWriter to write text to the socket stream, but then I needed to write the binary data. There is an easy solution, which works as-is for the case where binary data follows text data, and there is no more text data after the binary data.

Simply open a Printwriter on the stream and use the writer to print text into the stream until all the text is written. Then flush the PrintWriter to the stream, but don't close it (that closes the underlying stream, which must stay open). Lastly, write the binary data directly to the stream.

At the end you may simply close the PrintWriter to close the underlying stream.

If using the class provded below, you would:

  1. Construct the HttpStreamWriterImpl by providing the OutputStream for the underlying Socket.
  2. Call writeLine() repeatedly as needed.
  3. Call writeBinary() if/as needed.
  4. Call close() when finished.

Example:

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();
    }
}
Felipa answered 6/12, 2018 at 3:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.