57

I'm trying to read an image from an URL (with the Java package java.net.URL) to a byte[]. "Everything" works fine, except that the content isn't being entirely read from the stream (the image is corrupt, it doesn't contain all the image data)... The byte array is being persisted in a database (BLOB). I really don't know what the correct approach is, maybe you can give me a tip. :)

This is my first approach (code formatted, removed unnecessary information...):

URL u = new URL("http://localhost:8080/images/anImage.jpg");
int contentLength = u.openConnection().getContentLength();
Inputstream openStream = u.openStream();
byte[] binaryData = new byte[contentLength];
openStream.read(binaryData);
openStream.close();

My second approach was this one (as you'll see the contentlength is being fetched another way):

URL u = new URL(content);
openStream = u.openStream();
int contentLength = openStream.available();
byte[] binaryData = new byte[contentLength];
openStream.read(binaryData);
openStream.close();

Both of the code result in a corrupted image... I already read this post from Stack Overflow.

10 Answers 10

69

There's no guarantee that the content length you're provided is actually correct. Try something akin to the following:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
try {
  is = url.openStream ();
  byte[] byteChunk = new byte[4096]; // Or whatever size you want to read in at a time.
  int n;

  while ( (n = is.read(byteChunk)) > 0 ) {
    baos.write(byteChunk, 0, n);
  }
}
catch (IOException e) {
  System.err.printf ("Failed while reading bytes from %s: %s", url.toExternalForm(), e.getMessage());
  e.printStackTrace ();
  // Perform any other exception handling that's appropriate.
}
finally {
  if (is != null) { is.close(); }
}

You'll then have the image data in baos, from which you can get a byte array by calling baos.toByteArray().

This code is untested (I just wrote it in the answer box), but it's a reasonably close approximation to what I think you're after.

Sign up to request clarification or add additional context in comments.

6 Comments

Please never write an empty catch-block, not even in an example! Put at least e.printStackTrace() there! Examples have a tendency to become production code and we all have to work with that later on.
You're absolutely right; thanks for pointing that out. I've added more meaningful exception handling to the example.
Use commons.apache.org/io/api-1.4/org/apache/commons/io/… . This will make code look much cleaner.
Thx for your answer, your approach did work :) I have to write the data bit by bit (or a defined chunk value) to a ByteArrayOutputStream (which will on end be outputted with .toByteArray(), that was my fault...
In my app this code hangs on is.read(byteChunk) in some situations and never continious.
|
37

Just extending Barnards's answer with commons-io. Separate answer because I can not format code in comments.

InputStream is = null;
try {
  is = url.openStream ();
  byte[] imageBytes = IOUtils.toByteArray(is);
}
catch (IOException e) {
  System.err.printf ("Failed while reading bytes from %s: %s", url.toExternalForm(), e.getMessage());
  e.printStackTrace ();
  // Perform any other exception handling that's appropriate.
}
finally {
  if (is != null) { is.close(); }
}

http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toByteArray(java.io.InputStream)

2 Comments

Thats another nice solution, but I will use the first one (because we wont include too much external libs).
this solution does not work for me - Server returned HTTP response code: 403 for URL: templatelab.com/wp-content/uploads/2015/11/Coupon-50.jpg
27

Here's a clean solution:

private byte[] downloadUrl(URL toDownload) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    try {
        byte[] chunk = new byte[4096];
        int bytesRead;
        InputStream stream = toDownload.openStream();

        while ((bytesRead = stream.read(chunk)) > 0) {
            outputStream.write(chunk, 0, bytesRead);
        }

    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }

    return outputStream.toByteArray();
}

1 Comment

Don't forget to call the close method on 'stream' to release the used resources.
17

I am very surprised that nobody here has mentioned the problem of connection and read timeout. It could happen (especially on Android and/or with some crappy network connectivity) that the request will hang and wait forever.

The following code (which also uses Apache IO Commons) takes this into account, and waits max. 5 seconds until it fails:

public static byte[] downloadFile(URL url)
{
    try {
        URLConnection conn = url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        conn.connect(); 

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(conn.getInputStream(), baos);

        return baos.toByteArray();
    }
    catch (IOException e)
    {
        // Log error and return null, some default or throw a runtime exception
    }
}

Comments

16

Use commons-io IOUtils.toByteArray(URL):

String url = "http://localhost:8080/images/anImage.jpg";
byte[] fileContent = IOUtils.toByteArray(new URL(url));

Maven dependency:

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

Comments

12
byte[] b = IOUtils.toByteArray((new URL( )).openStream()); //idiom

Note however, that stream is not closed in the above example.

if you want a (76-character) chunk (using commons codec)...

byte[] b = Base64.encodeBase64(IOUtils.toByteArray((new URL( )).openStream()), true);

Comments

2

It's important to specify timeouts, especially when the server takes to respond. With pure Java, without using any dependency:

public static byte[] copyURLToByteArray(final String urlStr,
        final int connectionTimeout, final int readTimeout) 
                throws IOException {
    final URL url = new URL(urlStr);
    final URLConnection connection = url.openConnection();
    connection.setConnectTimeout(connectionTimeout);
    connection.setReadTimeout(readTimeout);
    try (InputStream input = connection.getInputStream();
            ByteArrayOutputStream output = new ByteArrayOutputStream()) {
        final byte[] buffer = new byte[8192];
        for (int count; (count = input.read(buffer)) > 0;) {
            output.write(buffer, 0, count);
        }
        return output.toByteArray();
    }
}

Using dependencies, e.g., HC Fluent:

public byte[] copyURLToByteArray(final String urlStr,
        final int connectionTimeout, final int readTimeout)
                throws IOException {
    return Request.Get(urlStr)
            .connectTimeout(connectionTimeout)
            .socketTimeout(readTimeout)
            .execute()
            .returnContent()
            .asBytes();
}

Comments

1

The content length is just a HTTP header. You cannot trust it. Just read everything you can from the stream.

Available is definitely wrong. It's just the number of bytes that can be read without blocking.

Another issue is your resource handling. Closing the stream has to happen in any case. try/catch/finally will do that.

1 Comment

Thx for your answer. I omit the try/catch in my code posting. But how can I know the exact length of the stream? I have to allocate the byte[], so I do have to provide a length. Allocate a fixed length (say 1024) and reading from an position to an offset, check if stream contains data, copying to a new array, merging all byte[] couldnt be the best solution...
0

Using dependencies, e.g., hutool-bom:

import java.io.InputStream;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.URLUtil;

public final class PdfUtil {

    private static byte[] readBytes(String urlStr) {
        URL url = URLUtil.toUrlForHttp(urlStr);
        InputStream inputStream = URLUtil.getStream(url);
        return IoUtil.readBytes(inputStream, true);
    }

}

Comments

0

Here is a complete solution from my application that reads hundreds of images to insert into generated documents. Each image is read into a byte array by URI. NULL is returned if any error occurs. You can modify the code to raise an exception if an error occurs.

BTW, this solution is taken from the same thread (see a few posts above) and slightly modified to correctly free up resources. Works fine!

private byte[] downloadUrl(URI targetSource) {
    try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
        try (final InputStream inputStream = targetSource.toURL().openStream()) {
            final byte[] chunk = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(chunk)) > 0) 
                outputStream.write(chunk, 0, bytesRead);
            return outputStream.toByteArray();
        } catch (IOException e) {
        }
    } catch (IOException e) {
    }
    return null;
}

3 Comments

The posting had following issues. 1) the second try-catch block was unnecessary. 2) it's best practice to place the contents of a while-loop in {} (and single-line if's too). 3) it's best practice to ensure the OutputStream is closed (explicitly or implicitly) before reading from it (even though, in this case, with a ByteArrayOutputStream the content is immediately available). 4) make final things final (it's not about performance, but rather, aids understanding). 5) return the null from within the catch-clause shows that only an Exception will return null. I've posted a correction.
P.S. I've also changed it to throw any IOException as the original question had no requirement to return null.
Hi Dave! Thanks for the comments on my example. I can't agree with all the comments, but I took some of the most important ones into account. I'm making changes to the example and to my code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.