简体   繁体   中英

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

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?

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.

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.

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).

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.

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();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM