How to reduce writing between InputStreams and OutputStreams? - java

I have the following use case:
read from service InputStream
go through the InputStream and replace some stuff, result is stored in OutputStream
now I need to go on working with an InputStream created from the OutputStream
This is the code I use right now:
InputStream resourceStream = service.getStream();
ByteArrayOutputStream output = new ByteArrayOutputStream();
replace(resourceStream, output, ...);
resourceStream.close();
resourceStream = new ByteArrayInputStream(out.toByteArray());
A lot of shifting and converting streams, so I was wondering if there is a cleaner solution. Maybe some OutputStream which can be used as InputStream, or an OutputStream which contains an InputStream where the content is written to.

No matter how good your idea of having a single object to write and read data from, the implementations of InputStream and OutputStream have been made as "Classes" and not "Interfaces".
Also, due to the fact that, in Java, a single subclass cannot extend multiple Super Classes, the dream of having both Input and Output stream operations in a single class remains just a dream.
That said, the only other option left for programmers would be to create a class that has both InputStream and OutputStream exposed to its clients. Something similar to what as java.net.Socket does. It exposes a getInputStream and a getOutputStream which at a logical level reads from and writes to the same "socket".
So, you can do something like this:
public class IOStreamWrapper {
byte[] streamData;
public InputStream getInputStream() {
// return an inputstream that reads from streamData[]
}
public OutputStream getOutputStream() {
// return an outputstream that writes to streamData[]
}
}
References:
Java InputStream
Java OutputStream
Hope this helps!

Related

converting OutputStream into an InputStream

Is there any way to convert an OutputStream into an InputStream?
So the following would work
InputStream convertOStoIS(OutputStream os) {
}
I do not want to use any libraries, I read that there are some who are able to accomplish this with bytecode manipulation.
Edit
I want to be able to intersect a sink, to analyze the data or redirect the output. I want to place another OutputStream under the on given by some function and redirect the data into another input stream.
The related topics had a ByteArrayOutputStream or a PipedStream which is not the case in my question.
Related:
How to convert OutputStream to InputStream?
Most efficient way to create InputStream from OutputStream
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.
To answer my own question, yes you can build a redirect like this:
class OutInInputRedirect {
public final transient InputStream is;
public final transient OutputStream os;
public OutInInputRedirect() throws IOException {
this(1024);
}
public OutInInputRedirect(int size) throws IOException {
PipedInputStream is = new PipedInputStream(size);
PipedOutputStream os = new PipedOutputStream(is);
this.is = is;
this.os = os;
}
}
Just use the OutputStream as an replacement and the InputStream in those places you need, be awere that the closing of the OutputStream also closes the InputStream!
It is quite easy and works as expected. Either way you cannot change an already connected stream (without reflection).

Spring OuputStream as File Download?

I would like to send OutputStream object, which has pdf data, as file to the webbrowser.
The code is as follows.
#RequestMapping(value="/issue", method=RequestMethod.POST)
public void issue(HttpServletResponse response, TimeStampIssueParam param) throws JsonGenerationException, JsonMappingException, IOException {
OutputStream pdfOuput = issue(input);
response.setContentType("application/pdf");
ServletOutputStream respOutput = response.getOutputStream();
....
}
The problem is I already have the outputstream, and I do not want to convert it to byte array.
Any comment would be appreciated.
You can't: you can only copy an InputStream to an OutputStream. Then, you'll can use: org.springframework.util.FileCopyUtils.copy(InputStream, OutputStream)
First, I would say it is wrong to say that the OutputStream has any data. A stream just lets the data through to some destination. Sometimes (SocketOutputStream) this destination may be on a completely different computer, and sometimes (ByteArrayOutputStream) it will be closely related to the stream and even obtainable through it. But this is a detail of a specific stream, not something you can count on from an arbitrary one.
So, not knowing exactly where the result of the issue method comes from it is hard to provide a solution, but a generic OutputStream is not what it should return.
Guessing that the method generates some PDF data and writes it somewhere via an OutputStream, then returns the stream:
If it creates a File and the stream happens to be a FileOutputStream, it should return the file, file path or a FileInputStream for the same file instead.
If it creates eg. a ByteArrayOutputStream, then you already have a byte array, and additionally this stream type has a writeTo method that can be used directly to write the data to the ServletOutputStream; issue just has to return the stream as the proper type not hiding it behind the general interface.
For other OutputStream types, well, it depends on what exactly they are.

Conversion between InputStream and BufferedImage

I want to convert an InputStream object representing an image file to a BufferedImage object and after performing some operations on the BufferedImage convert it back to an InputStream so that it can be written to disk.I dont want to create a file object on disk first in order to prevent additional IO overhead.
I think i can do the following to convert a BufferedImage to InputStream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image,fileExtension, outputStream);
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
Is that correct ?. Also, i have the following two questions
How to get BufferedImage object from an InputStream object
Can i get the filesize directly from the InputStream object ?
Some example would really help
Thank You
Take a look at the read(InputStream stream) method of ImageIO
No, you can have a peek using available() but this does not guarantee the size of the stream (it works for FileInputStream)
You can't write to an input stream (as its name states it, it is an input, not an output). To write, you need an OutputStream and you can use the write(RenderedImage im, String formatName, OutputStream output) of ImageIO

Write string to output stream

I have several output listeners that are implementing OutputStream.
It can be either a PrintStream writing to stdout or to a File, or it can be writing to memory or any other output destination; therefore, I specified OutputStream as (an) argument in the method.
Now, I have received the String. What is the best way to write to streams here?
Should I just use Writer.write(message.getBytes())? I can give it bytes, but if the destination stream is a character stream then will it convert automatically?
Do I need to use some bridge streams here instead?
Streams (InputStream and OutputStream) transfer binary data. If you want to write a string to a stream, you must first convert it to bytes, or in other words encode it. You can do that manually (as you suggest) using the String.getBytes(Charset) method, but you should avoid the String.getBytes() method, because that uses the default encoding of the JVM, which can't be reliably predicted in a portable way.
The usual way to write character data to a stream, though, is to wrap the stream in a Writer, (often a PrintWriter), that does the conversion for you when you call its write(String) (or print(String)) method. The corresponding wrapper for InputStreams is a Reader.
PrintStream is a special OutputStream implementation in the sense that it also contain methods that automatically encode strings (it uses a writer internally). But it is still a stream. You can safely wrap your stream with a writer no matter if it is a PrintStream or some other stream implementation. There is no danger of double encoding.
Example of PrintWriter with OutputStream:
try (PrintWriter p = new PrintWriter(new FileOutputStream("output-text.txt", true))) {
p.println("Hello");
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
OutputStream writes bytes, String provides chars. You need to define Charset to encode string to byte[]:
outputStream.write(string.getBytes(Charset.forName("UTF-8")));
Change UTF-8 to a charset of your choice.
You can create a PrintStream wrapping around your OutputStream and then just call it's print(String):
final OutputStream os = new FileOutputStream("/tmp/out");
final PrintStream printStream = new PrintStream(os);
printStream.print("String");
printStream.close();
By design it is to be done this way:
OutputStream out = ...;
try (Writer w = new OutputStreamWriter(out, "UTF-8")) {
w.write("Hello, World!");
} // or w.close(); //close will auto-flush
Wrap your OutputStream with a PrintWriter and use the print methods on that class. They take in a String and do the work for you.
You may use Apache Commons IO:
try (OutputStream outputStream = ...) {
IOUtils.write("data", outputStream, "UTF-8");
}
IOUtils.write(String data, OutputStream output, String encoding)

Open InputStream as Reader

Can I easily convert InputStream to BufferedReader using Guava?
I'm looking for something like:
InputStream inputStream = ...;
BufferedReader br = Streams.newBufferedReader(inputStream);
I can open files using the Files.newReader(File file, Charset charset). That's cool and I want to do the same using the InputStream.
UPDATE:
Using CharStreams.newReaderSupplier seems to verbose for me. Correct me if I'm wrong, but in order to easily convert InputStream to BufferedReader using Guava I have to do something like that:
final InputStream inputStream = new FileInputStream("/etc/fstab");
Reader bufferedReader = new BufferedReader(CharStreams.newReaderSupplier(new InputSupplier<InputStream>(){
public InputStream getInput() throws IOException {
return inputStream;
}
}, Charset.defaultCharset()).getInput());
Of course I can create helper do sth like:
return new BufferedReader(new InputStreamReader(inputStream));
However I think that such helper should be offered by Guava IO. I can do such trick for File instance. Why cannot I for InputStream?
// Guava can do this
Reader r = Files.newReader(new File("foo"), charset);
// but cannot do this
Reader r = SomeGuavaUtil.newReader(inputStream, charset);
Correct me If I'm wrong but it seems to me like lack in the API.
No, there isn't anything quite like that in Guava. CharStreams is the general class for working with Readers and Writers and it has a method
InputSupplier<InputStreamReader> newReaderSupplier(
InputSupplier<? extends InputStream> in, Charset charset)
which could be useful with any kind of supplier of InputStreams.
Obviously, you can just write new BufferedReader(new InputStreamReader(in, charset)) or wrap that in your own factory method as well.
Edit:
Yes, you wouldn't want to use the InputSupplier version when you already have an InputStream. It's sort of like how it's a bad idea to make an Iterable that can actually only work once, such as one that wraps an existing Iterator or Enumeration or some such. In general, using InputSupplier requires thinking about how you do I/O a little different, such as thinking of a File as something that can act as a supplier of FileInputStreams. I've used InputSuppliers that wrap whole requests to a server and return the response content as an InputStream, enabling me to use Guava utilities to copy that to a file, etc.
In any case, I'm not entirely sure why CharStreams doesn't have a method to create a Reader from an InputStream other than perhaps they didn't feel it was needed. You may want to file an issue requesting this.

Categories

Resources