51

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.

Null
  • 1,950
  • 9
  • 30
  • 33
tim.kaufner
  • 1,247
  • 5
  • 13
  • 22

8 Answers8

66

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.

Community
  • 1
  • 1
RTBarnard
  • 4,374
  • 27
  • 27
  • 48
    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. – Joachim Sauer Feb 19 '10 at 09:56
  • 4
    You're absolutely right; thanks for pointing that out. I've added more meaningful exception handling to the example. – RTBarnard Feb 19 '10 at 10:02
  • Use http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toByteArray(java.io.InputStream) . This will make code look much cleaner. – Adisesha Feb 19 '10 at 10:12
  • 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... – tim.kaufner Feb 19 '10 at 10:14
  • In my app this code hangs on `is.read(byteChunk)` in some situations and never continious. – Roel Mar 23 '16 at 09:49
  • I changed `in = new BufferedInputStream(url.openStream());` and now it is working without hanging. – Roel Mar 23 '16 at 10:07
35

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)

Adisesha
  • 5,200
  • 1
  • 32
  • 43
  • Thats another nice solution, but I will use the first one (because we wont include too much external libs). – tim.kaufner Feb 19 '10 at 10:36
  • this solution does not work for me - Server returned HTTP response code: 403 for URL: http://templatelab.com/wp-content/uploads/2015/11/Coupon-50.jpg – Javo Feb 12 '19 at 14:15
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();
}
Ron Reiter
  • 3,852
  • 3
  • 30
  • 34
16

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
    }
}
NumberFour
  • 3,551
  • 8
  • 48
  • 72
13

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>
alaster
  • 3,821
  • 3
  • 24
  • 32
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);
Y123
  • 915
  • 13
  • 30
jeff
  • 165
  • 1
  • 3
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.

Thomas Jung
  • 32,428
  • 9
  • 84
  • 114
  • 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... – tim.kaufner Feb 19 '10 at 09:56
1

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();
}
Paul Vargas
  • 41,222
  • 15
  • 102
  • 148