Android - downloading image from web, saving to internal memory in location private to app, displaying for list item

25,380

Solution 1

Looks like simply referring to the image file name when trying to read it was not enough, and I had to call getFilesDir() to get the path of the file storage. Below is the code I used:

String path = context.getFilesDir().toString();
String fileName = cursor.getString(cursor.getColumnIndex(DbAdapter.KEY_PRODUCT_ID));

if (fileName != null && !fileName.equals("")) {
    Bitmap bMap = BitmapFactory.decodeFile(path + "/" + fileName);
    if (bMap != null) {
        thumbnail.setImageBitmap(bMap);
    }
}

Solution 2

it seems that some code is left out, I re-wrote it like this:

ProductUtils.java

public static String productLookup(String productID, Context c) throws IOException {

    URL url = new URL("http://www.samplewebsite.com/" + productID + ".jpg");

    InputStream input = null;
    FileOutputStream output = null;

    try {
        String outputName = productID + "-thumbnail.jpg";

        input = url.openConnection().getInputStream();
        output = c.openFileOutput(outputName, Context.MODE_PRIVATE);

        int read;
        byte[] data = new byte[1024];
        while ((read = input.read(data)) != -1)
            output.write(data, 0, read);

        return outputName;

    } finally {
        if (output != null)
            output.close();
        if (input != null)
            input.close();
    }
}
Share:
25,380
Keeb13r
Author by

Keeb13r

Updated on August 18, 2020

Comments

  • Keeb13r
    Keeb13r almost 4 years

    What I'm trying to do is this: I want my application to download an image from the Internet and save it to the phone's internal memory in a location that is private to the application. If there is no image available for the list item (i.e. it can't be found on the Internet), I want a default placeholder image to display. This is the image that I have defined in my list_item_row.xml file as the default.

    In my ListActivity file, I am calling an instance of a CustomCursorAdapter class I have written. It is in CustomCursorAdapter where I am iterating through all the list items and defining what content needs to be mapped to the views, including the image file by trying to read it from internal memory.

    I've seen several questions on this subject, but the examples either are specific to external phone memory (e.g. SDCard), involve saving strings instead of images, or involve using Bitmap.CompressFormat to reduce the resolution of the file (which is unnecessary in my case, as these images will be small thumbnails of already-small resolution). Trying to piece together code from each example has been difficult, hence my asking about my specific example.

    At the moment, I believe I've written valid code, but no image is displaying for my list items, including the default placeholder image. I don't know if the problem is being caused by invalid download/save code, or invalid read code - it doesn't help that I don't know how to check internal memory to see if the image exists.

    Anyways, here's my code. Any help would be greatly appreciated.

    ProductUtils.java

    public static String productLookup(String productID, Context c) throws IOException {
        URL url = new URL("http://www.samplewebsite.com/" + productID + ".jpg");
        URLConnection connection = url.openConnection();
        InputStream input = connection.getInputStream();
        FileOutputStream output = 
            c.openFileOutput(productID + "-thumbnail.jpg", Context.MODE_PRIVATE);
        byte[] data = new byte[1024];
    
        output.write(data);
        output.flush();
        output.close();
        input.close();
    }
    

    CustomCursorAdapter.java

    public class CustomCursorAdapter extends CursorAdapter {
        public CustomCursorAdapter(Context context, Cursor c) {
            super(context, c);
        }
    
        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            ImageView thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
    
            String fileName = 
                    cursor.getString(cursor.getColumnIndex(DbAdapter.KEY_IMAGE_FILE_PATH));
    
            Bitmap bMap = BitmapFactory.decodeFile(fileName);
            thumbnail.setImageBitmap(bMap);
        }
    
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater inflater = LayoutInflater.from(context);
            View v = inflater.inflate(R.layout.list_item_row, parent, false);
            bindView(v, context, cursor);
            return v;
        }
    }
    
  • Keeb13r
    Keeb13r over 13 years
    The application won't compile with that code. It says that the input and output variables need to be initialized. I corrected to problematic lines to say "InputStream input = null" and "FileOutputStream output = null", but I'm still having the same problem with no image displaying.
  • Keeb13r
    Keeb13r over 13 years
    Looks like the error I'm getting is this: "java.io.IOException: Is a directory". I'm not sure what to think, since I thought that merely specifying the filename without introducing any intervening folders would result in the write and read path being exactly the same.
  • dacwe
    dacwe over 13 years
    Hum, does productID contain slashes (/)?
  • Keeb13r
    Keeb13r over 13 years
    Nope, it can only contain numbers and sometimes, an X to represent a 10. Is it possible I'm not reading the file from the proper location, i.e. when I call decodeFile(), I need to tell it the path where that file will be?
  • dacwe
    dacwe over 13 years
    Write to the log so you are sure that you have write and read the same file?
  • dacwe
    dacwe over 13 years
    Also, instead of writing the file to internal storage and storing the filename in a database, just download the file and show it directly in the view (just to be sure that you are downloading the correct file). Maybe the productID + "-thumbnail.jpg" does not exist on the server and you end up writing garbage to the internal storage!
  • Keeb13r
    Keeb13r over 13 years
    How would this be any different from what I'm currently doing? If I'm downloading the image to the phone's internal memory and then displaying it directly in the view, my app will still be reading the image from the exact same location it would if it was looking up the path from my DB.
  • dacwe
    dacwe over 13 years
    Then my question was valid: "Write to the log so you are sure that you have write and read the same file?".
  • Keeb13r
    Keeb13r over 13 years
    Originally, I wasn't aware that anything more than the filename was needed and I thought that the application was self-aware of the specific path where the file would be.
  • Keeb13r
    Keeb13r over 13 years
    One thing I forgot to ask - are there any catch statements I need in the downloading image block of code? What if the URL can't have a connection opened to it? What if there is no input stream to retrieve? Don't catch statements need to
  • dacwe
    dacwe over 13 years
    Well, if productLookup throws an exception you need to work out what should happen.. use a default image perhaps?
  • Keeb13r
    Keeb13r over 13 years
    What types of exceptions should I be throwing, though? Instantiating both the "input" and "output" variables can fail, can't they? Same goes for the reading/writing portion of that code block.
  • IcedDante
    IcedDante about 7 years
    This code is not really relevant to the question and does not even compile. I do think there's value in outlining the algorithm to persist the bitmap for future use, however.