Search code examples
androidexceptiontry-catchuniversal-image-loaderfileoutputstream

Save Images to external public storage using Universal Image Downloader?


I'm attempting to save images to the external SD card using Universal Image Loader. I was following this extremely outdated code fragment from the author, nostra, here:

Download Image stream from android universal image loader

I'm interested in recreating this successfully, but when I tried, I got

this error:

    06-21 07:56:22.586  17647-17647/as;dlkfa E/libEGL﹕ call to OpenGL ES API with no current context (logged once per thread)
06-21 07:56:27.562  17647-17647/;alsdjf E/ViewRootImpl﹕ sendUserActionEvent() mView == null
06-21 07:56:30.965  17647-17647/;aldjf; E/ViewRootImpl﹕ sendUserActionEvent() mView == null
06-21 07:56:32.737  17647-17647/a;sdjfl E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.NullPointerException
            at com.nostra13.universalimageloader.utils.IoUtils.copyStream(IoUtils.java:73)
            at com.nostra13.universalimageloader.utils.IoUtils.copyStream(IoUtils.java:50)
            at ;a;ldsfkj.MediaDisplayActivity.saveToGallery(MediaDisplayActivity.java:344)
            at as;dlkfj.MediaDisplayActivity.onMenuItemClick(MediaDisplayActivity.java:298)
            at android.widget.PopupMenu.onMenuItemSelected(PopupMenu.java:142)
            at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735)
            at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
            at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874)
            at com.android.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:175)
            at android.widget.AdapterView.performItemClick(AdapterView.java:301)
            at android.widget.AbsListView.performItemClick(AbsListView.java:1507)
            at android.widget.AbsListView$PerformClick.run(AbsListView.java:3336)
            at android.os.Handler.handleCallback(Handler.java:730)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5455)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
            at dalvik.system.NativeStart.main(Native Method)

I've changed his original code to be this. It is being called on a menu item click:

    private void saveToGallery()
{
    String imageUrl = imageUrls.get(pager.getCurrentItem());

    File path = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
    File fileForImage = new File(path, imageUrl);

    InputStream sourceStream = null;
    File cachedImage = ImageLoader.getInstance().getDiskCache().get(imageUrl);
    if (cachedImage.exists()) { // if image was cached by UIL
        try {
            sourceStream = new FileInputStream(cachedImage);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    } else { // otherwise - download image
        ImageDownloader downloader = new BaseImageDownloader(this);
        try {
            sourceStream = downloader.getStream(imageUrl, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // TODO : use try-finally to close streams
    OutputStream targetStream = null;
    try {
        targetStream = new FileOutputStream(fileForImage);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    try {
        IoUtils.copyStream(sourceStream, targetStream, null);
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        targetStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        sourceStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Line 344 is:

IoUtils.copyStream(sourceStream, targetStream, null);

Read comments for fix. Basically, I was using bad characters like https**:**

Also, here's my new code, it works, but It still won't create a album for your app which is pretty desirable if you want users to navigate quickly through their own albums. I'll keep begging for help from the coding gods in the comments. Also, it doesn't handle when saving the same image yet.

    //TODO upon uninstall ensure the files are still there
    //TODO make sure this janky stuff don't crash if you save the same stuff twice
    private boolean saveToGallery() {
        String imageUrl = imageUrls.get(pager.getCurrentItem());

        int i = imageUrl.length()-1;
        while(imageUrl.charAt(i) != '/')
        {
            i--;
        }
        String newImageUrl = imageUrl.substring(i+1);
        File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

        File fileForImage = new File(path, newImageUrl);
//        if (!fileForImage.mkdirs()) {
//            Log.e("shit", "Directory not created");
//        }

        boolean saved = false;

        InputStream sourceStream = null;
        File cachedImage = ImageLoader.getInstance().getDiskCache().get(imageUrl);
        try {
            if (cachedImage != null && cachedImage.exists()) { // if image was cached by UIL
                sourceStream = new FileInputStream(cachedImage);
            } else { // otherwise - download image
                ImageDownloader downloader = new BaseImageDownloader(this);
                sourceStream = downloader.getStream(imageUrl, null);
            }
        } catch (IOException e) {
            L.e(e);
        }

        if (sourceStream != null) {
            try {
                System.out.println(fileForImage.getAbsolutePath());
                OutputStream targetStream = new FileOutputStream(fileForImage);
                IoUtils.copyStream(sourceStream, targetStream, null);
                targetStream.close();
                sourceStream.close();
                saved = true;
            } catch (IOException e) {
                L.e(e);
            }
        }

        //Updates the gallery, dunno how though lol, yay SO
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
        return saved;

    }

Alright everyone, all I had to do was move the mkdirs() call before the appending of the url file. I'm too tired right now to figure out the ordering of things, but it is surely due to that. Here's the new code. This will save an image to an album in your app if you are using universal image loader and part of the URL for the file name, otherwise, no soup for you:

//TODO upon uninstall ensure the files are still there
//TODO make sure this janky stuff don't crash if you save the same stuff twice
private boolean saveToGallery() {
    String imageUrl = imageUrls.get(pager.getCurrentItem());

    int i = imageUrl.length()-1;
    while(imageUrl.charAt(i) != '/')
    {
        i--;
    }
    String newImageUrl = imageUrl.substring(i+1);
    File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES + "/Your app name here");

    if (!path.mkdirs()) {
        Log.e("shit", "Directory not created");
    }
    File fileForImage = new File(path, newImageUrl);


    boolean saved = false;

    InputStream sourceStream = null;
    File cachedImage = ImageLoader.getInstance().getDiskCache().get(imageUrl);
    try {
        if (cachedImage != null && cachedImage.exists()) { // if image was cached by UIL
            sourceStream = new FileInputStream(cachedImage);
        } else { // otherwise - download image
            ImageDownloader downloader = new BaseImageDownloader(this);
            sourceStream = downloader.getStream(imageUrl, null);
        }
    } catch (IOException e) {
        L.e(e);
    }

    if (sourceStream != null) {
        try {
            System.out.println(fileForImage.getAbsolutePath());
            OutputStream targetStream = new FileOutputStream(fileForImage);
            IoUtils.copyStream(sourceStream, targetStream, null);
            targetStream.close();
            sourceStream.close();
            saved = true;
        } catch (IOException e) {
            L.e(e);
        }
    }

    //Updates the gallery, dunno how though lol, yay SO
    sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
    return saved;

}

Solution

  • User#### it was a pleasure to help you. If all OP's were that talkative and informing!