为了账号安全,请及时绑定邮箱和手机立即绑定

如何在Android中下载和保存图像

/ 猿问

如何在Android中下载和保存图像

摇曳的蔷薇 2019-07-22 14:53:40

如何在Android中下载和保存图像

如何下载并保存Android中给定的url中的图像?



查看完整描述

3 回答

?
函数式编程

跳到这篇文章的底部,复制BasicImageDownloader(javadoc版本)这里)到您的项目中,实现OnImageLoaderListener接口你就完事了。

*虽然BasicImageDownloader处理可能出现的错误,并防止应用程序崩溃,以防发生任何错误,它不会对下载的程序执行任何后处理(例如缩小规模)。Bitmaps.


由于这篇文章受到了相当多的关注,我决定彻底修改它,以防止人们使用不推荐的技术、糟糕的编程实践或做一些愚蠢的事情-比如寻找“黑客”在主线程上运行网络或接受所有SSL证书。

我创建了一个名为“图像下载器”的演示项目,演示如何使用我自己的下载器实现(Android的内置)下载(并保存)图像DownloadManager以及一些流行的开源库。您可以查看完整的源代码或下载项目。论GitHub.

:我还没有调整SDK 23+(Marshmlow)的权限管理,因此该项目的目标是SDK 22(Lolliop)。

在我的结语在这篇文章的结尾,我将与大家分享我的卑微意见关于我提到的每一种特定图像下载方式的正确用例。

让我们从自己的实现开始(您可以在文章的末尾找到代码)。首先,这是基本ImageDownLoader-就是这样。它所做的就是连接到给定的url,读取数据并尝试将其解码为Bitmap,触发OnImageLoaderListener接口回调(适当时)。这种方法的优点-它很简单,你有一个清楚的概述发生了什么。如果您所需要的只是下载/显示和保存一些映像,而您并不关心维护内存/磁盘缓存,那么这是一个很好的方法。

注意:对于大型图像,您可能需要把它们缩小.

--

安卓DownloadManager是一种让系统为您处理下载的方法。它实际上可以下载任何类型的文件,而不仅仅是图像。您可以让您的下载对用户来说是无声的和不可见的,或者您可以让用户在通知区域中看到下载。您还可以注册BroadcastReceiver在下载完成后得到通知。设置非常简单,参考链接项目获取示例代码。

使用DownloadManager如果您也想显示图像,通常不是一个好主意,因为您需要读取和解码保存的文件,而不仅仅是设置下载的Bitmap变成ImageView..这个DownloadManager也没有为您的应用程序提供任何API来跟踪下载进度。

--

现在介绍一些伟大的东西-图书馆。他们可以做的不仅仅是下载和显示图像,包括:创建和管理内存/磁盘缓存、调整图像大小、转换图像等等。

我先从截击,这是一个强大的库,由Google创建,并由官方文档覆盖。虽然Volley是一个通用的网络库,不专门处理图像,但它为管理图像提供了一个非常强大的API。

您将需要实现辛格尔顿类来管理截击请求,您就可以继续了。

你可能想替换你的ImageView截击NetworkImageView,所以下载基本上变成了一个一行:

((NetworkImageView) findViewById(R.id.myNIV)).setImageUrl(url, MySingleton.getInstance(this).getImageLoader());

如果您需要更多的控制,这就是创建一个ImageRequest截击:

     ImageRequest imgRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
             @Override
             public void onResponse(Bitmap response) {
                    //do stuff
                }
            }, 0, 0, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888, 
             new Response.ErrorListener() {
             @Override
             public void onErrorResponse(VolleyError error) {
                   //do stuff
                }
            });

值得一提的是,volley通过提供VolleyError类,它帮助您确定错误的确切原因。如果你的应用程序做了很多的网络和管理图像不是它的主要目的,那么截击它是一个完美的适合你。

--

正方形毕加索是一个著名的库,它将为您完成所有的图像加载工作。使用Picasso显示图像非常简单,如下所示:

 Picasso.with(myContext)
       .load(url)
       .into(myImageView);

默认情况下,Picasso管理磁盘/内存缓存,因此不需要担心这个问题。有关更多控件,您可以实现Target接口,并使用它将映像加载到-这将提供类似于volley示例的回调。有关示例,请查看演示项目。

Picasso还允许您对下载的映像应用转换,甚至其他图书馆围绕这一点扩展了那些API。在RecyclerView/ListView/GridView.

--

通用图像装载机是另一个非常流行的图书馆,服务于形象管理的目的。它使用自己的ImageLoader该程序(一旦初始化)具有一个全局实例,可用于在一行代码中下载图像:

  ImageLoader.getInstance().displayImage(url, myImageView);

如果要跟踪下载进度或访问下载的Bitmap:

 ImageLoader.getInstance().displayImage(url, myImageView, opts, 
 new ImageLoadingListener() {
     @Override
     public void onLoadingStarted(String imageUri, View view) {
                     //do stuff
                }

      @Override
      public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
                   //do stuff
                }

      @Override
      public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                   //do stuff
                }

      @Override
      public void onLoadingCancelled(String imageUri, View view) {
                   //do stuff
                }
            }, new ImageLoadingProgressListener() {
      @Override
      public void onProgressUpdate(String imageUri, View view, int current, int total) {
                   //do stuff
                }
            });

这个opts参数在此示例中是DisplayImageOptions对象。请参考演示项目了解更多信息。

类似于volley,UIL提供了FailReason类,使您能够检查下载失败时出错的地方。默认情况下,如果没有显式告诉UIL不要这样做,UIL将维护内存/磁盘缓存。

提交人提到,自2015年11月27日起,他不再维持该项目。但由于有许多贡献者,我们可以希望通用图像加载器将继续存在。

--

Facebook的壁画是最新和(国际海事组织)最先进的图书馆,将图像管理提升到一个新的水平:从保持Bitmaps从java堆(在Lolliop之前)到支持动画格式渐进JPEG流.

要了解更多关于弗雷斯科背后的想法和技术,请参阅这个职位.

基本用法相当简单。请注意,您需要打电话给Fresco.initialize(Context);只有一次,最好在Application班级,等级。多次初始化Fresco可能会导致不可预测的行为和OOM错误。

壁画用途Drawee如果要显示图像,您可以将它们想象为ImageViews:

    <com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/drawee"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    fresco:fadeDuration="500"
    fresco:actualImageScaleType="centerCrop"
    fresco:placeholderImage="@drawable/placeholder_grey"
    fresco:failureImage="@drawable/error_orange"
    fresco:placeholderImageScaleType="fitCenter"
    fresco:failureImageScaleType="centerInside"
    fresco:retryImageScaleType="centerCrop"
    fresco:progressBarImageScaleType="centerInside"
    fresco:progressBarAutoRotateInterval="1000"
    fresco:roundAsCircle="false" />

正如您所看到的,很多东西(包括转换选项)已经在XML中定义了,所以显示图像所需要做的就是一个一行:

 mDrawee.setImageURI(Uri.parse(url));

FRECO提供了一个扩展的自定义API,在某些情况下,这个API可能非常复杂,并且需要用户仔细阅读文档(是的,有时是这样)。你需要(RTFM)

我已经在示例项目中包括了渐进式JPEG和动画图像的示例。


结论-“我已经了解了这些伟大的东西,我现在该用什么呢?”

请注意,以下案文反映了我的个人意见,不应被视为一种假设。

  • 如果您只需要下载/保存/显示一些图像,则不要计划在

    Recycler-/Grid-/ListView

    而且不需要大量的图像就可以显示,

    BasicImageDownload

    应该能满足你的需要。
  • 如果您的应用程序由于用户或自动操作而保存图像(或其他文件),并且不需要经常显示这些图像,请使用Android

    DownloadManager.

  • 如果您的应用程序进行了大量的联网,发送/接收

    JSON

    数据,与图像一起工作,但这些不是应用程序的主要用途,请与

    截击.

  • 您的应用程序是以图像/媒体为中心的,您希望将一些转换应用于图像,而不想费心使用复杂的api:use

    毕加索

    (注意:不提供任何api来跟踪中间下载状态)或

    通用图像装载机

  • 如果您的应用程序都是关于图像的,那么您需要高级功能,比如显示动画格式,并且您已经准备好阅读文档了。

    壁画.

如果你错过了,GitHub链接演示项目。


这是BasicImageDownloader.java

import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.support.annotation.NonNull;import android.util.Log;import java.io.BufferedInputStream;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.URL;import java.net.URLConnection;import java.util.HashSet;import java.util.Set;public class BasicImageDownloader {

    private OnImageLoaderListener mImageLoaderListener;
    private Set<String> mUrlsInProgress = new HashSet<>();
    private final String TAG = this.getClass().getSimpleName();

    public BasicImageDownloader(@NonNull OnImageLoaderListener listener) {
        this.mImageLoaderListener = listener;
    }

    public interface OnImageLoaderListener {
        void onError(ImageError error);      
        void onProgressChange(int percent);
        void onComplete(Bitmap result);
    }


    public void download(@NonNull final String imageUrl, final boolean displayProgress) {
        if (mUrlsInProgress.contains(imageUrl)) {
            Log.w(TAG, "a download for this url is already running, " +
                    "no further download will be started");
            return;
        }

        new AsyncTask<Void, Integer, Bitmap>() {

            private ImageError error;

            @Override
            protected void onPreExecute() {
                mUrlsInProgress.add(imageUrl);
                Log.d(TAG, "starting download");
            }

            @Override
            protected void onCancelled() {
                mUrlsInProgress.remove(imageUrl);
                mImageLoaderListener.onError(error);
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                mImageLoaderListener.onProgressChange(values[0]);
            }

        @Override
        protected Bitmap doInBackground(Void... params) {
            Bitmap bitmap = null;
            HttpURLConnection connection = null;
            InputStream is = null;
            ByteArrayOutputStream out = null;
            try {
                connection = (HttpURLConnection) new URL(imageUrl).openConnection();
                if (displayProgress) {
                    connection.connect();
                    final int length = connection.getContentLength();
                    if (length <= 0) {
                        error = new ImageError("Invalid content length. The URL is probably not pointing to a file")
                                .setErrorCode(ImageError.ERROR_INVALID_FILE);
                        this.cancel(true);
                    }
                    is = new BufferedInputStream(connection.getInputStream(), 8192);
                    out = new ByteArrayOutputStream();
                    byte bytes[] = new byte[8192];
                    int count;
                    long read = 0;
                    while ((count = is.read(bytes)) != -1) {
                        read += count;
                        out.write(bytes, 0, count);
                        publishProgress((int) ((read * 100) / length));
                    }
                    bitmap = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.size());
                } else {
                    is = connection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(is);
                }
            } catch (Throwable e) {
                if (!this.isCancelled()) {
                    error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
                    this.cancel(true);
                }
            } finally {
                try {
                    if (connection != null)
                        connection.disconnect();
                    if (out != null) {
                        out.flush();
                        out.close();
                    }
                    if (is != null)
                        is.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return bitmap;
        }

            @Override
            protected void onPostExecute(Bitmap result) {
                if (result == null) {
                    Log.e(TAG, "factory returned a null result");
                    mImageLoaderListener.onError(new ImageError("downloaded file could not be decoded as bitmap")
                            .setErrorCode(ImageError.ERROR_DECODE_FAILED));
                } else {
                    Log.d(TAG, "download complete, " + result.getByteCount() +
                            " bytes transferred");
                    mImageLoaderListener.onComplete(result);
                }
                mUrlsInProgress.remove(imageUrl);
                System.gc();
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    public interface OnBitmapSaveListener {
        void onBitmapSaved();
        void onBitmapSaveError(ImageError error);
    }


    public static void writeToDisk(@NonNull final File imageFile, @NonNull final Bitmap image,
                               @NonNull final OnBitmapSaveListener listener,
                               @NonNull final Bitmap.CompressFormat format, boolean shouldOverwrite) {

    if (imageFile.isDirectory()) {
        listener.onBitmapSaveError(new ImageError("the specified path points to a directory, " +
                "should be a file").setErrorCode(ImageError.ERROR_IS_DIRECTORY));
        return;
    }

    if (imageFile.exists()) {
        if (!shouldOverwrite) {
            listener.onBitmapSaveError(new ImageError("file already exists, " +
                    "write operation cancelled").setErrorCode(ImageError.ERROR_FILE_EXISTS));
            return;
        } else if (!imageFile.delete()) {
            listener.onBitmapSaveError(new ImageError("could not delete existing file, " +
                    "most likely the write permission was denied")
                    .setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
            return;
        }
    }

    File parent = imageFile.getParentFile();
    if (!parent.exists() && !parent.mkdirs()) {
        listener.onBitmapSaveError(new ImageError("could not create parent directory")
                .setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
        return;
    }

    try {
        if (!imageFile.createNewFile()) {
            listener.onBitmapSaveError(new ImageError("could not create file")
                    .setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
            return;
        }
    } catch (IOException e) {
        listener.onBitmapSaveError(new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION));
        return;
    }

    new AsyncTask<Void, Void, Void>() {

        private ImageError error;

        @Override
        protected Void doInBackground(Void... params) {
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(imageFile);
                image.compress(format, 100, fos);
            } catch (IOException e) {
                error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
                this.cancel(true);
            } finally {
                if (fos != null) {
                    try {
                        fos.flush();
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        @Override
        protected void onCancelled() {
            listener.onBitmapSaveError(error);
        }

        @Override
        protected void onPostExecute(Void result) {
            listener.onBitmapSaved();
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }public static Bitmap readFromDisk(@NonNull File imageFile) {
    if (!imageFile.exists() || imageFile.isDirectory()) return null;
    return BitmapFactory.decodeFile(imageFile.getAbsolutePath());}public interface OnImageReadListener {
    void onImageRead(Bitmap bitmap);
    void onReadFailed();}public static void readFromDiskAsync(@NonNull File imageFile, @NonNull final OnImageReadListener listener) {
    new AsyncTask<String, Void, Bitmap>() {
        @Override
        protected Bitmap doInBackground(String... params) {
            return BitmapFactory.decodeFile(params[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null)
                listener.onImageRead(bitmap);
            else
                listener.onReadFailed();
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageFile.getAbsolutePath());}

    public static final class ImageError extends Throwable {

        private int errorCode;
        public static final int ERROR_GENERAL_EXCEPTION = -1;
        public static final int ERROR_INVALID_FILE = 0;
        public static final int ERROR_DECODE_FAILED = 1;
        public static final int ERROR_FILE_EXISTS = 2;
        public static final int ERROR_PERMISSION_DENIED = 3;
        public static final int ERROR_IS_DIRECTORY = 4;


        public ImageError(@NonNull String message) {
            super(message);
        }

        public ImageError(@NonNull Throwable error) {
            super(error.getMessage(), error.getCause());
            this.setStackTrace(error.getStackTrace());
        }

        public ImageError setErrorCode(int code) {
            this.errorCode = code;
            return this;
        }

        public int getErrorCode() {
            return errorCode;
        }
      }
   }



查看完整回答
反对 回复 2019-07-22
?
慕姐8265434

我刚刚解决了这个问题,我想分享完整的代码,这些代码可以下载、保存到sdCard(并隐藏文件名)并检索图像,最后它检查图像是否已经存在。url来自数据库,因此文件名可以很容易地使用id。

第一次下载图片

   private class GetImages extends AsyncTask<Object, Object, Object> {
    private String requestUrl, imagename_;
    private ImageView view;
    private Bitmap bitmap ; 
      private FileOutputStream fos;
    private GetImages(String requestUrl, ImageView view, String _imagename_) {
        this.requestUrl = requestUrl;
        this.view = view;
        this.imagename_ = _imagename_ ;
    }

    @Override
    protected Object doInBackground(Object... objects) {
        try {
            URL url = new URL(requestUrl);
            URLConnection conn = url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
        } catch (Exception ex) {
        }
        return null;
    }

    @Override
    protected void onPostExecute(Object o) { 
        if(!ImageStorage.checkifImageExists(imagename_))
        { 
                view.setImageBitmap(bitmap);
                ImageStorage.saveToSdCard(bitmap, imagename_); 
            }  
        }  
   }

然后创建一个用于保存和检索文件的类。

  public class ImageStorage {public static String saveToSdCard(Bitmap bitmap, String filename) {

    String stored = null;

    File sdcard = Environment.getExternalStorageDirectory() ; 

    File folder = new File(sdcard.getAbsoluteFile(), ".your_specific_directory");//the dot makes this directory hidden to the user
    folder.mkdir(); 
    File file = new File(folder.getAbsoluteFile(), filename + ".jpg") ;
    if (file.exists())
        return stored ;

    try {
        FileOutputStream out = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
        out.flush();
        out.close();
        stored = "success";
    } catch (Exception e) {
        e.printStackTrace();
    }
     return stored;
   }public static File getImage(String imagename) {

        File mediaImage = null;
        try {
            String root = Environment.getExternalStorageDirectory().toString();
            File myDir = new File(root);
            if (!myDir.exists())
                return null;

            mediaImage = new File(myDir.getPath() + "/.your_specific_directory/"+imagename);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return mediaImage;
    }public static boolean checkifImageExists(String imagename){
    Bitmap b = null ;
    File file = ImageStorage.getImage("/"+imagename+".jpg");
    String path = file.getAbsolutePath();

    if (path != null)
        b = BitmapFactory.decodeFile(path); 

    if(b == null ||  b.equals(""))
    {
        return false ;
    }
    return true ;}
  }

然后,首先访问图像,检查它是否已经在那里,如果没有,然后下载。

          if(ImageStorage.checkifImageExists(imagename))
            {
                File file = ImageStorage.getImage("/"+imagename+".jpg"); 
                String path = file.getAbsolutePath(); 
                if (path != null){
                    b = BitmapFactory.decodeFile(path);
                    imageView.setImageBitmap(b);
                }  
            } else { 
                new GetImages(imgurl, imageView, imagename).execute() ; 
            }


查看完整回答
反对 回复 2019-07-22
?
回首忆惘然

为什么你真的需要自己的代码来下载呢?把URI传递给下载管理器怎么样?

public void downloadFile(String uRl) {
    File direct = new File(Environment.getExternalStorageDirectory()
            + "/AnhsirkDasarp");

    if (!direct.exists()) {
        direct.mkdirs();
    }

    DownloadManager mgr = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);

    Uri downloadUri = Uri.parse(uRl);
    DownloadManager.Request request = new DownloadManager.Request(
            downloadUri);

    request.setAllowedNetworkTypes(
            DownloadManager.Request.NETWORK_WIFI                    | DownloadManager.Request.NETWORK_MOBILE)
            .setAllowedOverRoaming(false).setTitle("Demo")
            .setDescription("Something useful. No, really.")
            .setDestinationInExternalPublicDir("/AnhsirkDasarp", "fileName.jpg");

    mgr.enqueue(request);}


查看完整回答
反对 回复 2019-07-22

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信