Androidで画像をダウンロードして保存する方法

ドロイドマン

Androidの特定のURLから画像をダウンロードして保存するにはどうすればよいですか?

ドロイドマン

2015年12月30日現在の編集-画像ダウンロードの究極のガイド


最終メジャーアップデート:2016年3月31日


TL; DR別名、話をやめて、コードを教えてください!!

この投稿の最後にスキップし、BasicImageDownloaderここではjavadocバージョン)をプロジェクトにコピーしOnImageLoaderListenerインターフェースを実装すれば完了です。

BasicImageDownloaderエラーの可能性を処理し、問題が発生した場合にアプリがクラッシュするのを防ぎますが、ダウンロードした後処理(ダウンサイジングなど)は実行しませんBitmaps


この投稿は非常に多くの注目を集めているので、人々が非推奨のテクノロジー、悪いプログラミング慣行を使用したり、メインスレッドでネットワークを実行するための「ハック」を探すなどの愚かなことをしたりしないように、完全に作り直すことにしました。すべてのSSL証明書を受け入れます。

「ImageDownloader」という名前のデモプロジェクトを作成しました。これは、独自のダウンローダー実装、Androidの組み込みDownloadManager、およびいくつかの人気のあるオープンソースライブラリを使用して画像をダウンロード(および保存)する方法を示しています完全なソースコードを表示するか、GitHubでプロジェクトダウンロードできます

:SDK 23+(Marshmallow)の権限管理はまだ調整していないため、プロジェクトはSDK 22(Lollipop)を対象としています。

私の中で結論この記事の最後に、私は共有することになり、私の愚見私が述べてきた画像のダウンロードの各特定の方法のための適切なユースケースについてを。

独自の実装から始めましょう(コードは投稿の最後にあります)。まず第一に、これは基本的なImageDownloaderであり、それだけです。指定されたURLに接続し、データを読み取り、それをとしてデコードしようとし、必要に応じBitmapOnImageLoaderListenerインターフェイスコールバックをトリガーするだけです。このアプローチの利点-それは単純であり、何が起こっているのかを明確に把握できます。必要なのは、メモリ/ディスクキャッシュの維持を気にせずに、いくつかの画像をダウンロード/表示して保存することだけである場合に最適な方法です。

注:大きな画像の場合は、縮小する必要がある場合があります

-

Android DownloadManagerは、システムにダウンロードを処理させる方法です。実際には、画像だけでなく、あらゆる種類のファイルをダウンロードできます。ダウンロードをサイレントに実行してユーザーに表示しないようにすることも、ユーザーが通知領域にダウンロードを表示できるようにすることもできます。BroadcastReceiverダウンロードが完了した後に通知を受け取るためにを登録することもできます。セットアップは非常に簡単です。サンプルコードについては、リンクされたプロジェクトを参照してください。

DownloadManagerダウンロードBitmapしたファイルをに設定するだけでなく、保存したファイルを読み取ってデコードする必要があるため、画像も表示する場合は、通常、を使用することはお勧めできませんImageViewまたDownloadManager、ダウンロードの進行状況を追跡するためのアプリ用のAPIも提供していません。

-

さて、素晴らしいもの、つまりライブラリの紹介です。メモリ/ディスクキャッシュの作成と管理、画像のサイズ変更、画像の変換など、画像のダウンロードと表示だけではありません。

まず、Googleによって作成され、公式ドキュメントでカバーされている強力なライブラリであるVolleyから始めます。Volleyは、画像に特化していない汎用ネットワークライブラリでありながら、画像を管理するための非常に強力なAPIを備えています。

ボレーリクエストを管理するためにシングルトンクラスを実装する必要があります。

あなたはあなたImageViewをボレーのものに置き換えたいかもしれませんNetworkImageView、それでダウンロードは基本的にワンライナーになります:

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

より詳細な制御が必要な場合はImageRequest、Volleyを使用しを作成する次のようになります

     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の正確な原因を特定するのに役立つクラスを提供することにより、優れたエラー処理メカニズムを備えていることは言及する価値があります。アプリが多くのネットワーキングを行い、画像の管理が主な目的ではない場合は、Volleyが最適です。

-

SquareのPicassoは、画像の読み込みをすべて行う有名なライブラリです。ピカソを使用して画像を表示するだけで、次のように簡単になります。

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

デフォルトでは、ピカソはディスク/メモリキャッシュを管理するので、それについて心配する必要はありません。より詳細に制御するには、Targetインターフェースを実装し、それを使用して画像をロードします。これにより、Volleyの例と同様のコールバックが提供されます。例については、デモプロジェクトを確認してください。

Picassoでは、ダウンロードした画像に変換を適用することもできます。また、これらのAPIを拡張する他のライブラリもあります。また、非常にうまく機能RecyclerView/ ListView/ GridView

-

Universal Image Loaderは、画像管理を目的としたもう1つの非常に人気のあるライブラリです。ImageLoader(初期化されると)独自のグローバルインスタンスを使用します。このインスタンスを使用して、1行のコードで画像をダウンロードできます。

  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は、明示的に指示しない限り、メモリ/ディスクキャッシュを維持します。

:著者は、2015年11月27日をもってプロジェクトを維持しなくなったと述べています。しかし、多くの貢献者がいるため、Universal ImageLoaderが存続することを期待できます。

-

FacebookのFrescoは、画像管理を新しいレベルに引き上げる最新かつ(IMO)最先端のライブラリですBitmaps。Javaヒープ(Lollipopより前)をオフにすることから、アニメーション形式プログレッシブJPEGストリーミングをサポートすることまで

Frescoの背後にあるアイデアとテクニックの詳細については、この投稿を参照しください

基本的な使い方はとても簡単です。呼び出す必要があるのはFresco.initialize(Context);1回だけで、Applicationクラスでの使用が望ましいことに注意してくださいFrescoを複数回初期化すると、予期しない動作やOOMエラーが発生する可能性があります。

FrescoはDraweesを使用して画像を表示しますが、次のように考えることができますImageView

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

Frescoは拡張カスタマイズAPIを提供しますが、これは状況によっては非常に複雑になる可能性があり、ユーザーはドキュメントを注意深く読む必要があります(はい、RTFMが必要な場合もあります)。

プログレッシブJPEGとアニメーション画像の例をサンプルプロジェクトに含めました。


結論-「私は素晴らしいものについて学びました、私は今何を使うべきですか?」

以下の文章は私の個人的な意見を反映したものであり、仮定として解釈されるべきではないことに注意してください。

  • 一部の画像をダウンロード/保存/表示するだけでよく、Recycler-/Grid-/ListViewそれらを使用する予定がなく、表示できるようにするために大量の画像を必要としない場合は、BasicImageDownloaderがニーズに合うはずです。
  • ユーザーまたは自動アクションの結果としてアプリが画像(または他のファイル)を保存し、画像を頻繁に表示する必要がない場合は、AndroidDownloadManagerを使用してください
  • アプリが多くのネットワーキングを行い、JSONデータ送受信し、画像を処理するが、それらがアプリの主な目的ではない場合は、Volleyを使用してください
  • アプリは画像/メディアに重点を置いており、画像にいくつかの変換を適用したいが、複雑なAPIに煩わされたくない:Picasso(注:中間ダウンロードステータスを追跡するためのAPIは提供されていません)またはUniversal Imageローダ
  • アプリがすべて画像に関するものである場合は、アニメーション形式の表示などの高度な機能が必要であり、ドキュメントを読む準備ができている場合は、Frescoを使用してください

それを見逃した場合は、デモプロジェクトの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;
        }
      }
   }

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Androidでsvg画像をダウンロードして表示する

分類Dev

Androidでphonegap / cordovaを使用してファイルをダウンロードして保存する方法

分類Dev

html / javascriptを使用して「名前を付けて画像を保存」する代わりに画像をインラインでダウンロードする方法

分類Dev

RxAlamofireを使用して画像をダウンロードする方法

分類Dev

http経由でAndroidキャッシュに画像をダウンロードして後で表示する方法は?

分類Dev

MNIST画像をPNGとしてダウンロードする方法

分類Dev

URLからGIF画像をダウンロードして、アンドロイドでストレージに保存する方法

分類Dev

SDWebImageを使用してCGImageをダウンロードして保存する方法

分類Dev

外部ストレージに画像を保存するURLからピカソを使用して、画像をダウンロードしてimageviewに表示する方法

分類Dev

bytearrayを使用してサーバー側からAndroidに画像をダウンロードする方法は?

分類Dev

URLSessionを使用してビデオをダウンロードし、appceleratorTitaniumで保存して再生する方法は?

分類Dev

ファイルを作成してダウンロードフォルダandroid内に保存する方法は?

分類Dev

ImageLoadで画像をダウンロードした後にURLをダウンロードする(Android)

分類Dev

Android のダウンロード フォルダーに画像を保存する

分類Dev

(方法)JSoupを使用して画像をダウンロードできますか?

分類Dev

ダウンロードした画像をアプリ指定のフォルダーに正しく保存する方法

分類Dev

リクエストを使用して画像をダウンロードする方法

分類Dev

Scrapyを使用して画像をダウンロードする方法は?

分類Dev

nscacheを使用してAPIから画像をダウンロードする方法は?

分類Dev

AndroidのURLから画像をダウンロードする方法

分類Dev

Awesomiumは、ロードした画像を再ダウンロードせずに保存する方法

分類Dev

URL配列を読み取るすべての画像をダウンロードして保存する

分類Dev

Xamarin-Formsを使用して画像をダウンロードしてローカルストレージに保存する方法。

分類Dev

TCPベースのHTTPを使用してPythonで画像をダウンロードする方法は?

分類Dev

RのURLから画像をダウンロードして表示する方法は?

分類Dev

PHPでダウンロードを強制してダウンロードをカウントする方法

分類Dev

R:rvestを使用して画像をダウンロードする

分類Dev

FetchAPIを使用して画像をダウンロードする

分類Dev

Retrofit andGlideを使用して画像をダウンロードする

Related 関連記事

  1. 1

    Androidでsvg画像をダウンロードして表示する

  2. 2

    Androidでphonegap / cordovaを使用してファイルをダウンロードして保存する方法

  3. 3

    html / javascriptを使用して「名前を付けて画像を保存」する代わりに画像をインラインでダウンロードする方法

  4. 4

    RxAlamofireを使用して画像をダウンロードする方法

  5. 5

    http経由でAndroidキャッシュに画像をダウンロードして後で表示する方法は?

  6. 6

    MNIST画像をPNGとしてダウンロードする方法

  7. 7

    URLからGIF画像をダウンロードして、アンドロイドでストレージに保存する方法

  8. 8

    SDWebImageを使用してCGImageをダウンロードして保存する方法

  9. 9

    外部ストレージに画像を保存するURLからピカソを使用して、画像をダウンロードしてimageviewに表示する方法

  10. 10

    bytearrayを使用してサーバー側からAndroidに画像をダウンロードする方法は?

  11. 11

    URLSessionを使用してビデオをダウンロードし、appceleratorTitaniumで保存して再生する方法は?

  12. 12

    ファイルを作成してダウンロードフォルダandroid内に保存する方法は?

  13. 13

    ImageLoadで画像をダウンロードした後にURLをダウンロードする(Android)

  14. 14

    Android のダウンロード フォルダーに画像を保存する

  15. 15

    (方法)JSoupを使用して画像をダウンロードできますか?

  16. 16

    ダウンロードした画像をアプリ指定のフォルダーに正しく保存する方法

  17. 17

    リクエストを使用して画像をダウンロードする方法

  18. 18

    Scrapyを使用して画像をダウンロードする方法は?

  19. 19

    nscacheを使用してAPIから画像をダウンロードする方法は?

  20. 20

    AndroidのURLから画像をダウンロードする方法

  21. 21

    Awesomiumは、ロードした画像を再ダウンロードせずに保存する方法

  22. 22

    URL配列を読み取るすべての画像をダウンロードして保存する

  23. 23

    Xamarin-Formsを使用して画像をダウンロードしてローカルストレージに保存する方法。

  24. 24

    TCPベースのHTTPを使用してPythonで画像をダウンロードする方法は?

  25. 25

    RのURLから画像をダウンロードして表示する方法は?

  26. 26

    PHPでダウンロードを強制してダウンロードをカウントする方法

  27. 27

    R:rvestを使用して画像をダウンロードする

  28. 28

    FetchAPIを使用して画像をダウンロードする

  29. 29

    Retrofit andGlideを使用して画像をダウンロードする

ホットタグ

アーカイブ