3

I'm trying to pull the image from a URL and read it directly into a ByteArrayInputStream. I found one way of doing it, but it requires an image type, and there will be various image types, so I'd like to find a simple way to just read the binary data right in.

Here is my latest attempt. I'm using a BufferedImage, which I don't think is necessary.

URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");

//Read in the image
BufferedImage image = ImageIO.read(url);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
is = new ByteArrayInputStream(baos.toByteArray());
BenW
  • 737
  • 10
  • 41
  • 1
    What is it you're really trying to accomplish? You have the Image object, so why throw it into an output stream to only redirect to another input stream? – Ryan May 04 '21 at 19:59
  • I'm trying to get the binary data passed to another process that will encode it for transmission in a json message. – BenW May 04 '21 at 20:39
  • Why? Complete waste of time and space. Just pass the input stream, or indeed the URL, to the other process. – user207421 May 11 '21 at 00:03
  • Why, indeed. This is a customization running inside an inefficient and inappropriate integration solution feeding and inefficient customized proprietary system that needs to store the image out of fear of latency. – BenW May 11 '21 at 13:02

3 Answers3

2
URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
url.openStream().transferTo(baos);

ByteArrayInputStream in = new ByteArrayInputStream(baos.toByteArray());

The transferTo() method exists since Java 9. If you should use an older version of Java please see here for an alternative. Main drawback of this solution is that it has to read the whole file into memory first. If you anyway plan to forward the binary data to an other process you could omit the ByteArray streams and transfer the content directly to an OutputStream.

rmunge
  • 3,653
  • 5
  • 19
1

As an alternative to the solution proposed by @rmunge, the Apache Commons IO library provides the class IOUtils which can be vey useful in your use case.

If you are using Maven for instance, you can import the library including the following dependency in your pom.xml:

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.8.0</version>
</dependency>

Then, you can use IOUtils like this:

URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");
try (
  InputStream imageInputStream = url.openStream();
  ByteArrayOutputStream bOut = new ByteArrayOutputStream()
) {
  // You can obtain a byte[] as well if required
  // Please, consider write to the actual final OutputStream instead
  // of into the intermediate byte array output stream to optimize memory
  // consumption
  IOUtils.copy(imageInputStream, bOut);

  // Create an input stream from the read bytes
  ByteArrayInputStream in = new ByteArrayInputStream(bOut.toByteArray());
  // ...
} catch (IOException ioe) {
  ioe.printStackTrace();
}

Or simply this approach:

URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");
byte[] imageBytes = IOUtils.toByteArray(url);
ByteArrayInputStream in = new ByteArrayInputStream(imageBytes);

For your comments, if the problem if you are trying to avoid network latency problems, if the requirement for a ByteArrayInputStream is not strictly necessary, as you can see in the javadocs perhaps the following code may be helpful as well:

URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");
try (InputStream imageInputStream = url.openStream()) {
  InputStream in = IOUtils.toBufferedInputStream(imageInputStream);
  //...
}

Of course, you can always perform the read and write "manually" using the standard Java InputStream and OutputStream mechanisms:

URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");
try (
  InputStream inputStream = url.openStream();
  BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
  ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); 
) {
  byte[] buffer = new byte[8192];
  int bytesRead;
  while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
    bufferedOutputStream.write(buffer, 0, bytesRead);
  }

  bufferedOutputStream.flush();

  // Create an input stream from the read bytes
  ByteArrayInputStream in = new ByteArrayInputStream(outputStream.toByteArray());
  // ...
} catch (IOException ioe) {
  ioe.printStackTrace();
}

If you require more control about the underlying URL connection you can use URLConnection or HttpURLConnection, or many HTTP client libraries like Apache HttpClient or OkHttp, to name some of them.

Take as example the problem pointed out by @LuisCarlos in his comment, in order to avoid possible leak connections:

URLConnection urlConn = null;
try {
  urlConn = url.openConnection();
  urlConn.setConnectTimeout(5000);
  urlConn.setReadTimeout(30000);
  InputStream inputStream = urlConn.getInputStream();
  // the rest of the code...

} catch (Exception e) {
  
}

If you need to detect the actual image type consider the use of Tika or JMimeMagic.

jccampanero
  • 50,989
  • 3
  • 20
  • 49
  • What exactly is the pont of creating anothe `InputStream` when you already have one? – user207421 May 11 '21 at 10:10
  • @user207421 Thank you for the feedback and I agree with you, but the OP indicated in the question that he/she has that requirement, and maybe the question is only showing part of the problem, and the answer try to address it. In addition, please, consider review the [javadoc](https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)) of the `IOUtils.toBufferedInputStream` for instance: as you can see, transforming one `InputStream` into another (more efficient), can be valuable in some situations. – jccampanero May 11 '21 at 10:15
  • @user207421 Please, as you can see, the answer try providing more information about the whole problem as well, not limited to the `InputStream` conversion itself. It also suggest the use of more convenient libraries for the interaction with HTTP in order to fetch the resource and for dealing with the actual image format itself it necessary. – jccampanero May 11 '21 at 10:20
  • There are some cases where a resource in a server (ex. an image) could not be readed for some reason, if you let "url.openStream();" you could wait the response almost forever. Instead is good idea setting connect-timeout and read-timeout to the connection before open it `URLConnection urlConn = null; try { urlConn = url.openConnection(); urlConn.setConnectTimeout(5000); urlConn.setReadTimeout(30000); } catch (Exception e) { }` then change this line of your code: `InputStream inputStream = urlConn.getInputStream();` – Luis Carlos Oct 08 '21 at 12:00
  • Thank you very much for the comment @LuisCarlos, I really appreciate it. I totally agree with you: the answer only try scratching the surface of the problem, providing several alternatives, but definitely there is a great room for improvement. Gracias!! – jccampanero Oct 08 '21 at 21:58
-2

Here's the solution I found to work. Thanks for the two approaches above. I'd rather avoid external libraries, but because the environment is a real pain. Similar, I should have access to Java 9 and transferTo(), but that's not working.

This answerer was also helpful: Convert InputStream(Image) to ByteArrayInputStream

URL url = new URL("http://hobbylesson.com/wp-content/uploads/2015/04/Simple-Acrylic-Painting-Ideas00005.jpg");
        
InputStream source = url.openStream();
byte[] buf = new byte[8192]; 
int bytesRead = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((bytesRead = source.read(buf)) != -1) {
     baos.write(buf, 0, bytesRead);
}
is = new ByteArrayInputStream(baos.toByteArray());
BenW
  • 737
  • 10
  • 41