Easy way to write contents of a Java InputStream to an OutputStream
Java 9
Since Java 9, InputStream
provides a method called transferTo
with the following signature:
public long transferTo(OutputStream out) throws IOException
As the documentation states, transferTo
will:
Reads all bytes from this input stream and writes the bytes to the
given output stream in the order that they are read. On return, this
input stream will be at end of stream. This method does not close
either stream.This method may block indefinitely reading from the
input stream, or writing to the output stream. The behavior for the
case where the input and/or output stream is asynchronously closed, or
the thread interrupted during the transfer, is highly input and output
stream specific, and therefore not specified
So in order to write contents of a Java InputStream
to an OutputStream
, you can write:
input.transferTo(output);
How to connect a input stream to a output stream in Java?
Guava and Apache Commons have copy methods:
ByteStreams.copy(input, output);
IOUtils.copy(input ,output);
They don't "connect" them directly. To achieve what I am assuming you want, create an InputStream
decorator class that writes to an OutputStream
everything that is read.
Most efficient way to create InputStream from OutputStream
If you don't want to copy all of the data into an in-memory buffer all at once then you're going to have to have your code that uses the OutputStream (the producer) and the code that uses the InputStream (the consumer) either alternate in the same thread, or operate concurrently in two separate threads. Having them operate in the same thread is probably much more complicated that using two separate threads, is much more error prone (you'll need to make sure that the consumer never blocks waiting for input, or you'll effectively deadlock) and would necessitate having the producer and consumer running in the same loop which seems way too tightly coupled.
So use a second thread. It really isn't that complicated. The page you linked to had reasonable example. Here's a somewhat modernized version, which also closes the streams:
try (PipedInputStream in = new PipedInputStream()) {
new Thread(() -> {
try (PipedOutputStream out = new PipedOutputStream(in)) {
writeDataToOutputStream(out);
} catch (IOException iox) {
// handle IOExceptions
}
}).start();
processDataFromInputStream(in);
}
How to convert OutputStream to InputStream?
An OutputStream
is one where you write data to. If some module exposes an OutputStream
, the expectation is that there is something reading at the other end.
Something that exposes an InputStream
, on the other hand, is indicating that you will need to listen to this stream, and there will be data that you can read.
So it is possible to connect an InputStream
to an OutputStream
InputStream----read---> intermediateBytes[n] ----write----> OutputStream
As someone metioned, this is what the copy()
method from IOUtils lets you do. It does not make sense to go the other way... hopefully this makes some sense
UPDATE:
Of course the more I think of this, the more I can see how this actually would be a requirement. I know some of the comments mentioned Piped
input/ouput streams, but there is another possibility.
If the output stream that is exposed is a ByteArrayOutputStream
, then you can always get the full contents by calling the toByteArray()
method. Then you can create an input stream wrapper by using the ByteArrayInputStream
sub-class. These two are pseudo-streams, they both basically just wrap an array of bytes. Using the streams this way, therefore, is technically possible, but to me it is still very strange...
Best way to Pipe InputStream to OutputStream
I would say a fixed buffer size is the best/easiest to understand. However there are a few problems.
You're writing the entire buffer to the output stream each time. For the final block the read may have read < 1024 bytes so you need to take this into account when doing the write (basically only write number of bytes returned by
read()
In the dynamic buffer case you use
available()
. This is not a terribly reliable API call. I'm not sure in this case inside a loop whether it will be ok, but I wouldn't be suprised if it was implemented sub-optimally in some implementations of InputStream.The last case you are casting to
FileInputStream
. If you intend for this to be general purpose then you can't use this approach.
connect OutputStream with an InputStream
Abstract/decorate the Request
as well and get the Response
from it instead.
E.g. in your front controller servlet:
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Request request = new RequestImpl(req, res);
Action action = ActionFactory.getAction(req); // Do whatever way you do to locate the `Action`.
Response = action.get(request);
// ...
}
wherein RequestImpl
look like this:
public class RequestImpl implements Request {
private HttpServletRequest request;
private HttpServletResponse response;
public RequestImpl(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
public Response newResponse(Status status) {
return new ResponseImpl(response, status);
// Add a boolean responseCreated to avoid creation of multiple responses? Illegal state!
}
public String getParameter(String name) { // Just another example of decorated method.
return request.getParameter(name);
}
// ...
}
and the ResponseImpl
look like this:
public class ResponseImpl implements Response {
private HttpServletResponse response;
public ResponseImpl(HttpServletResponse response, Status status) {
this.response = response;
this.response.setStatus(status.getCode());
}
public OutputStream getOutputStream() {
return response.getOutputStream();
}
// ...
}
which you finally use like this in your Action
:
public ActionImpl implements Action {
public Response get(Request request) {
Response response = request.newResponse(OK);
response.getOutputStream().write("body");
return response;
}
}
Alternatively, you can also create a Context
which takes both the HttpServletRequest
and HttpServletResponse
and pass that in instead of Request
. That's also less or more what the average MVC framework does. E.g.
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Context context = new ContextImpl(req, res);
Action action = ActionFactory.getAction(req); // Do whatever way you do to locate the `Action`.
action.execute(context);
context.render(); // Do here whatever you'd initially to do with the obtained Response.
}
with
public ActionImpl implements Action {
public void execute(Context context) {
context.getResponseOutputStream().write("body");
}
}
That said, instead of reinventing, I'd suggest to have a look to existing API's as well. Depending on what you'd like to do, JSF, JAX-RS or JAX-WS might be what you're actually after. Unless this is for pure hobby purposes ;)
converting OutputStream into an InputStream
Use a java.io.FilterOutputStream
to wrap the existing OutputStream
. By overriding the write()
method you can intercept output and do whatever you want with it, either send it somewhere else, modify it, or discard it completely.
As to your second question, you cannot change the sink of an OutputStream
after the fact, i.e. cause previously written data to "move" somewhere else, but using a FilterOutputStream
you can intercept and redirect any data written after you wrap the original `OutputStream.
Related Topics
Replace the Last Part of a String
Drawing in Jlayeredpane Over Exising JPAnels
Why Do We Assign a Parent Reference to the Child Object in Java
Differencebetween Class.This and This in Java
Shared Memory Between Two Jvms
How to Convert String to Date Without Knowing the Format
Java, "Variable Name" Cannot Be Resolved to a Variable
Swingworker, Thread.Sleep(), or Javax.Swing.Timer? I Need to "Insert a Pause"
System.Currenttimemillis() VS. New Date() VS. Calendar.Getinstance().Gettime()
How to Display a Svg Byte Array as an Image in a Jasperreport
What Is the Class Object (Java.Lang.Class)
Flutter Doctor --Android-Licenses Gives a Java Error
Gradle: Could Not Determine Java Version from '11.0.2'
Removing Invalid Xml Characters from a String in Java
How to Force Browser to Download File
Getting a Illegalblocksizeexception: Data Must Not Be Longer Than 256 Bytes When Using Rsa