【问题标题】:Save Images to external public storage using Universal Image Downloader?使用通用图像下载器将图像保存到外部公共存储?
【发布时间】:2014-08-12 01:52:07
【问题描述】:

我正在尝试使用 Universal Image Loader 将图像保存到外部 SD 卡。我在这里关注作者 nostra 的这个非常过时的代码片段:

Download Image stream from android universal image loader

我有兴趣成功地重新创建它,但是当我尝试时,我得到了

这个错误:

    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)

我把他原来的代码改成了这个。在菜单项单击时调用它:

    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();
    }
}

第 344 行是:

IoUtils.copyStream(sourceStream, targetStream, null);

读取 cmets 进行修复。基本上,我使用的是像 https**:**这样的坏字符

另外,这是我的新代码,它可以工作,但它仍然不会为您的应用创建相册,如果您希望用户快速浏览他们自己的相册,这是非常理想的。我将继续向 cmets 中的编码大神寻求帮助。另外,保存同一张图片的时候还不处理。

    //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;

    }

好的,我所要做的就是在附加 url 文件之前移动 mkdirs() 调用。我现在太累了,无法弄清楚事情的顺序,但肯定是因为这个。这是新代码。如果您使用通用图像加载器和文件名的部分 URL,这会将图像保存到应用程序的相册中,否则,您将无所适从:

//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;

}

【问题讨论】:

  • spanToGallery 中的第 344 行导致其中一个流出现 NullPointerException,您可以在 logcat 中读取。第 344 行是什么?
  • IoUtils.copyStream(sourceStream, targetStream, null);
  • 好的。现在,正如我已经说过的那样,其中一个流是空的。你发现是哪一个了吗?你的代码有问题。实例化 targetStream 的 try 块有一个 catch 块,以防出现问题。如果有问题,您应该返回那里,因为在这种情况下继续使用空指针毫无意义。
  • 奇怪的是,来自那个 catch 块的 e.printStackTrace() 没有出现在 logcat 中。无论如何,targetStream=.... 是罪魁祸首。因此fileForImage 为空或指示路径错误,或者您在清单中没有写入外部权限。请找出确切的完整路径。什么是 fileForImage.getAbsolutePath()?
  • 嗯,这是一条非常不寻常的道路。那不可能是您将其保存在那里的意图。首先,它不是有效路径,因为其中包含“:”。您已在存储目录上附加了以 http 开头的完整 url,您很可能只会附加以 .jpg 结尾的文件名。确实,您使用imageUrl 做到了。请先提取文件名。

标签: android exception-handling try-catch universal-image-loader fileoutputstream


【解决方案1】:

用户#### 很高兴为您提供帮助。如果所有 OP 都那么健谈和通知!

【讨论】:

    【解决方案2】:

    我重构了你的方法,如果保存到画廊失败,它会返回false

    private boolean saveToGallery() {
        String imageUrl = imageUrls.get(pager.getCurrentItem());
        File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File fileForImage = new File(path, imageUrl);
    
        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 {
                OutputStream targetStream = new FileOutputStream(fileForImage);
                try {   
                    IoUtils.copyStream(sourceStream, targetStream, null);
                    saved = true;
                } catch (IOException e) {
                    L.e(e);
                } finally {
                    targetStream.close();
                }
            } catch (IOException e) {
                L.e(e);
            } finally {
                sourceStream.close();
            }
        }
    
        return saved;
    }
    

    【讨论】:

    • 如果targetStream无法打开(就像这里的情况)刚刚打开的sourceStream将不会被关闭。
    • NOSTRA,你是一个活生生的男人中的传奇。您的库为我的应用程序减少了大约 90% 的工作量,因此您很有可能挽救了它的生命。一旦完成,我一定会相信你。现在,我必须回答 greenapps 让我在 cmets 中行走,如果他发帖的话。
    猜你喜欢
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 2016-08-06
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 2019-04-28
    相关资源
    最近更新 更多