0

I am pretty new to Android and making an app that requires a jpg from a url to be downloaded and cached for use.

I have so far from multiple references (and my own tinkering) got to this:

public class download_image extends AsyncTask<String, Void, String> {
    int count;
    @Override
    protected String doInBackground(String... urls) {
        try {
            String root = getFilesDir().toString();
            URL imgurl = new URL(urls[0]); // Form a URL object from string.
            URLConnection c = imgurl.openConnection();   //Open connection.
            int length_of_file = c.getContentLength();      // Get size of target jpg
            Log.v("Background Response","Length fo target img = "+length_of_file);
            InputStream i = new BufferedInputStream(imgurl.openStream(),length_of_file);

            //Make target image file...
            final File image = new File(getApplicationContext().getCacheDir().toString(),"image.jpg");

            OutputStream o = new FileOutputStream(image);
            byte data[] = new byte[length_of_file];
            long total = 0;
            while ((count = i.read(data))!=1){
                total+=count;
                o.write(data,0,count);
            }

            o.flush();
            o.close();
            i.close();

        } catch (MalformedURLException m) {
            Log.e("Exception", m.toString());
        } catch (IOException i) {
            Log.e("Exception", i.toString());
        }

        return null;
    }

    @Override
    protected void onPostExecute(String s) {
        System.out.println("Downloaded");
        super.onPostExecute(s);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
}

The problem seems to be that the array data[] over flows or something? I can not quite figure it out. I have tried making the InputStream's buffer equal to the file size from length_of_file = getContentLength();

Any help would be greatly appreciated! Gotten over other issues quite happily so far (like getting the internal folder to write to and what an Async Task is...which I originally didn't know I needed for HTTPConnections).

    08-04 15:51:22.938 13454-13486/com.example.johnny.fibre V/Background Response: Length fo target img = 106620
08-04 15:51:23.058 13454-13486/com.example.johnny.fibre E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #2
                                                                          Process: com.example.johnny.fibre, PID: 13454
                                                                          java.lang.RuntimeException: An error occurred while executing doInBackground()
                                                                              at android.os.AsyncTask$3.done(AsyncTask.java:325)
                                                                              at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
                                                                              at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
                                                                              at java.util.concurrent.FutureTask.run(FutureTask.java:242)
                                                                              at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
                                                                              at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
                                                                              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
                                                                              at java.lang.Thread.run(Thread.java:761)
                                                                           Caused by: java.lang.ArrayIndexOutOfBoundsException: length=106620; regionStart=0; regionLength=-1
                                                                              at java.util.Arrays.checkOffsetAndCount(Arrays.java:4857)
                                                                              at libcore.io.IoBridge.write(IoBridge.java:490)
                                                                              at java.io.FileOutputStream.write(FileOutputStream.java:316)
                                                                              at com.example.johnny.fibre.Home$download_image.doInBackground(Home.java:476)
                                                                              at com.example.johnny.fibre.Home$download_image.doInBackground(Home.java:456)
                                                                              at android.os.AsyncTask$2.call(AsyncTask.java:305)
                                                                              at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                                                                              at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) 
                                                                              at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
                                                                              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
                                                                              at java.lang.Thread.run(Thread.java:761) 
Jcov
  • 2,122
  • 2
  • 21
  • 32

1 Answers1

0

getContentLength() returns the length of the content header, and not the size of the image you're downloading. In this case, it is very likely that you're allocating an array that isn't big enough to fit the image you intend to write!

Instead of allocating a byte array, why not stream the data you read into a ByteArrayOutputStream, and fetch the result by calling getBytes() on the stream once you're finished?

By doing it this way, you won't be required to know the length of the image at all; all you'll need to do is keep reading data until you hit the end of the InputStream.

Click here for an example of how to use a ByteArrayOutputStream.

As an alternative, you might wish to take advantage of some helper utilities built into the Android runtime, specifically designed for fetching image data from a URL.

Mapsy
  • 4,192
  • 1
  • 37
  • 43
  • Thank you. I will look in to the ByteArrayOutputStream and return if I have any issues. I want to cache the images for future use rather than having to use an active internet socket each time I need an image...so the Helper Utilities seem to be out. – Jcov Aug 05 '17 at 12:46
  • If you use the helper utilities I referenced, you can make the online call once upon initialization, or when it is first needed, then store a reference to the image in a local model so you don't have to keep querying the network. – Mapsy Aug 06 '17 at 10:14