So laden Sie ein Bild in Android herunter und speichern es

Antworten:

224

Bearbeiten ab 30.12.2015 - Der ultimative Leitfaden zum Herunterladen von Bildern


Letztes großes Update: 31. März 2016


TL; DR aka hör auf zu reden, gib mir einfach den Code !!

Gehen Sie zum Ende dieses Beitrags, kopieren Sie die BasicImageDownloader(Javadoc-Version hier ) in Ihr Projekt, implementieren Sie die OnImageLoaderListenerSchnittstelle und fertig.

Hinweis : Obwohl die BasicImageDownloaderBehandlung mögliche Fehler behandelt und verhindert, dass Ihre App abstürzt, falls etwas schief geht, führt sie keine Nachbearbeitung (z. B. Verkleinerung) des heruntergeladenen Geräts durch Bitmaps.


Da dieser Beitrag viel Aufmerksamkeit erhalten hat, habe ich mich entschlossen, ihn komplett zu überarbeiten, um zu verhindern, dass die Leute veraltete Technologien, schlechte Programmierpraktiken oder einfach nur alberne Dinge verwenden - wie die Suche nach "Hacks", um das Netzwerk im Hauptthread oder auszuführen Akzeptieren Sie alle SSL-Zertifikate.

Ich habe ein Demo-Projekt namens "Image Downloader" erstellt, das zeigt, wie ein Bild mit meiner eigenen Downloader-Implementierung, dem integrierten Android-Gerät DownloadManagersowie einigen beliebten Open-Source-Bibliotheken heruntergeladen (und gespeichert) wird . Sie können den vollständigen Quellcode anzeigen oder das Projekt auf GitHub herunterladen .

Hinweis : Ich habe die Berechtigungsverwaltung für SDK 23+ (Marshmallow) noch nicht angepasst, daher zielt das Projekt auf SDK 22 (Lollipop) ab.

In meiner Schlussfolgerung am Ende dieses Beitrags werde ich meine bescheidene Meinung über den richtigen Anwendungsfall für jede bestimmte Art des Herunterladens von Bildern, die ich erwähnt habe, teilen .

Beginnen wir mit einer eigenen Implementierung (den Code finden Sie am Ende des Beitrags). Zunächst einmal ist dies ein Basic ImageDownloader und das wars. Es wird lediglich eine Verbindung zu der angegebenen URL hergestellt, die Daten gelesen und versucht, sie als zu dekodieren. Bei Bedarf werden die Rückrufe Bitmapder OnImageLoaderListenerSchnittstelle ausgelöst . Der Vorteil dieses Ansatzes - es ist einfach und Sie haben einen klaren Überblick darüber, was los ist. Ein guter Weg, wenn Sie nur einige Bilder herunterladen / anzeigen und speichern müssen, während Sie sich nicht darum kümmern, einen Speicher- / Festplatten-Cache zu verwalten.

Hinweis: Bei großen Bildern müssen Sie diese möglicherweise verkleinern .

- -

Mit Android DownloadManager kann das System den Download für Sie erledigen. Es ist tatsächlich in der Lage, jede Art von Dateien herunterzuladen, nicht nur Bilder. Sie können Ihren Download unbeaufsichtigt und für den Benutzer unsichtbar machen oder dem Benutzer ermöglichen, den Download im Benachrichtigungsbereich anzuzeigen. Sie können sich auch registrieren BroadcastReceiver, um benachrichtigt zu werden, nachdem der Download abgeschlossen ist. Das Setup ist ziemlich einfach. Beispielcode finden Sie im verknüpften Projekt.

Die Verwendung von DownloadManagerist im Allgemeinen keine gute Idee, wenn Sie das Bild auch anzeigen möchten, da Sie die gespeicherte Datei lesen und dekodieren müssen, anstatt nur die heruntergeladene Datei Bitmapin eine zu setzen ImageView. Das DownloadManagerbietet auch keine API für Ihre App, um den Download-Fortschritt zu verfolgen.

- -

Nun die Einführung der großartigen Dinge - der Bibliotheken. Sie können viel mehr als nur Bilder herunterladen und anzeigen, einschließlich: Erstellen und Verwalten des Speicher- / Festplatten-Caches, Ändern der Größe von Bildern, Transformieren und mehr.

Ich beginne mit Volley , einer leistungsstarken Bibliothek, die von Google erstellt und in der offiziellen Dokumentation behandelt wird. Volley ist zwar eine universelle Netzwerkbibliothek, die sich nicht auf Bilder spezialisiert hat, bietet jedoch eine leistungsstarke API zum Verwalten von Bildern.

Sie müssen eine Singleton- Klasse zum Verwalten von Volley-Anforderungen implementieren und können loslegen.

Vielleicht möchten Sie Ihre ImageViewdurch Volleys ersetzen NetworkImageView, damit der Download im Grunde genommen zu einem Einzeiler wird:

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

Wenn Sie mehr Kontrolle benötigen, sieht es so aus, als würden Sie ImageRequestmit Volley eine erstellen :

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

Es ist erwähnenswert, dass Volley über einen hervorragenden Mechanismus zur Fehlerbehandlung verfügt, indem es die VolleyErrorKlasse bereitstellt , mit der Sie die genaue Fehlerursache ermitteln können. Wenn Ihre App viel vernetzt und Bilder verwaltet, ist dies nicht der Hauptzweck von Volley. Dann passt Volley perfekt zu Ihnen.

- -

Square's Picasso ist eine bekannte Bibliothek, die das gesamte Laden von Bildern für Sie erledigt. Das Anzeigen eines Bildes mit Picasso ist so einfach wie:

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

Standardmäßig verwaltet Picasso den Festplatten- / Speicher-Cache, sodass Sie sich darüber keine Gedanken machen müssen. Für mehr Kontrolle können Sie die TargetSchnittstelle implementieren und zum Laden Ihres Bildes verwenden - dies liefert Rückrufe ähnlich dem Volley-Beispiel. Überprüfen Sie das Demo-Projekt für Beispiele.

Mit Picasso können Sie auch Transformationen auf das heruntergeladene Bild anwenden, und es gibt sogar andere Bibliotheken , die diese API erweitern. Funktioniert auch sehr gut in einem RecyclerView/ ListView/ GridView.

- -

Universal Image Loader ist eine weitere sehr beliebte Bibliothek, die der Bildverwaltung dient. Es verwendet eine eigene ImageLoaderInstanz, die (nach der Initialisierung) über eine globale Instanz verfügt, mit der Bilder in einer einzigen Codezeile heruntergeladen werden können:

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

Wenn Sie den Download-Fortschritt verfolgen oder auf die heruntergeladenen Dateien zugreifen möchten 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
                }
            });

Das optsArgument in diesem Beispiel ist ein DisplayImageOptionsObjekt. Weitere Informationen finden Sie im Demo-Projekt.

Ähnlich wie bei Volley bietet UIL die FailReasonKlasse, mit der Sie überprüfen können, was bei einem Downloadfehler fehlgeschlagen ist. Standardmäßig verwaltet UIL einen Speicher- / Festplatten-Cache, wenn Sie nicht ausdrücklich anweisen, dies nicht zu tun.

Hinweis : Der Autor hat erwähnt, dass er das Projekt ab dem 27. November 2015 nicht mehr verwaltet. Da es jedoch viele Mitwirkende gibt, können wir hoffen, dass der Universal Image Loader weiterleben wird.

- -

Facebooks Fresco ist die neueste und (IMO) fortschrittlichste Bibliothek, die die Bildverwaltung auf ein neues Niveau Bitmapshebt : vom Halten des Java-Heaps (vor Lollipop) bis zur Unterstützung von animierten Formaten und progressivem JPEG-Streaming .

Weitere Informationen zu Ideen und Techniken hinter Fresco finden Sie in diesem Beitrag .

Die grundlegende Verwendung ist recht einfach. Beachten Sie, dass Sie Fresco.initialize(Context);nur einmal anrufen müssen , vorzugsweise in der ApplicationKlasse. Das mehrmalige Initialisieren von Fresco kann zu unvorhersehbarem Verhalten und OOM-Fehlern führen.

Fresco verwendet Drawees, um Bilder anzuzeigen. Sie können sich diese als ImageViews vorstellen:

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

Wie Sie sehen können, sind viele Dinge (einschließlich Transformationsoptionen) bereits in XML definiert. Alles, was Sie tun müssen, um ein Bild anzuzeigen, ist ein Einzeiler:

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

Fresco bietet eine erweiterte Anpassungs-API, die unter Umständen sehr komplex sein kann und erfordert, dass der Benutzer die Dokumente sorgfältig liest (ja, manchmal müssen Sie RTFM verwenden).

Ich habe Beispiele für progressive JPEGs und animierte Bilder in das Beispielprojekt aufgenommen.


Fazit - "Ich habe etwas über die großartigen Dinge gelernt. Was soll ich jetzt verwenden?"

Beachten Sie, dass der folgende Text meine persönliche Meinung widerspiegelt und nicht als Postulat verstanden werden sollte.

  • Wenn Sie nur einige Bilder herunterladen / speichern / anzeigen müssen, nicht planen, sie in einem zu verwenden, Recycler-/Grid-/ListViewund nicht eine ganze Reihe von Bildern benötigen, um für die Anzeige bereit zu sein, sollte der BasicImageDownloader Ihren Anforderungen entsprechen.
  • Wenn Ihre App Bilder (oder andere Dateien) aufgrund eines Benutzers oder einer automatisierten Aktion speichert und Sie nicht möchten, dass die Bilder häufig angezeigt werden, verwenden Sie den Android DownloadManager .
  • Wenn Ihre App viel vernetzt, JSONDaten sendet / empfängt , mit Bildern arbeitet, diese aber nicht der Hauptzweck der App sind, entscheiden Sie sich für Volley .
  • Ihre App ist bild- / medienorientiert. Sie möchten einige Transformationen auf Bilder anwenden und sich nicht mit komplexen APIs beschäftigen: Verwenden Sie Picasso (Hinweis: bietet keine API zum Verfolgen des Download-Zwischenstatus) oder Universal Image Lader
  • Wenn es in Ihrer App nur um Bilder geht, benötigen Sie erweiterte Funktionen wie das Anzeigen animierter Formate und können die Dokumente lesen. Entscheiden Sie sich für Fresco .

Falls Sie das verpasst haben, klicken Sie auf den Github-Link für das Demo-Projekt.


Und hier ist die 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;
        }
      }
   }
Droidman
quelle
Was ist mit dem Rückruf onPictureTaken (), der das Bild als Byte [] angibt? Kann man eine URL zu diesem Bild direkt von der Kamera erhalten? Oder ist die einfache alte Ausgabe von outputStream () die einzige Möglichkeit in Android, ein Bild zu speichern, das von einer Kamera aufgenommen wurde, ohne die eingebaute Absicht zu verwenden? Das scheint seltsam, denn nach onPictureTaken ist es natürlich, es zu speichern. Gibt es dafür keine besondere Unterstützung?
Tombola
2
@ Tombola Hallo! In diesem Beitrag geht es darum, ein Bild aus dem Internet herunterzuladen. Aber um Ihre Frage zu beantworten (soweit ich es verstanden habe): Die übliche Methode zum Speichern eines Kamerabilds besteht darin, den Pfad von Cursor(in der onActivityResult()Methode) abzurufen und dann einen BitmapPfad zu erstellen , der diesen Pfad verwendet. Und ja, Sie werden nicht davon abkommen, a FileOutputStreamund a zu verwenden, ByteArrayOutputStreamwenn Sie dieses Image auf SD speichern möchten.
Droidman
@BartBurg Bei dieser Frage geht es um das Herunterladen und Speichern eines Bildes. Aber Sie haben irgendwann Recht, da es eine Schreibmethode gibt, sollte es der Vollständigkeit halber auch eine Lesemethode geben. Ich werde es in Kürze im nächsten Update zu diesem Beitrag hinzufügen.
Droidman
Können Sie bitte ein Beispiel mit diesem BasicImageDownloader angeben?
Jaydev
1
@ JaydevKalivarapu überprüfen Sie bitte die Demo-App auf GitHub (
Quellklasse
35

Ich bin gerade von der Lösung dieses Problems gekommen und möchte den vollständigen Code, der heruntergeladen werden kann, auf der SD-Karte speichern (und den Dateinamen ausblenden), die Bilder abrufen und schließlich prüfen, ob das Bild bereits vorhanden ist. Die URL stammt aus der Datenbank, sodass der Dateiname mit id eindeutig eindeutig verwendet werden kann.

Bilder zuerst herunterladen

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

Erstellen Sie dann eine Klasse zum Speichern und Abrufen der Dateien

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

Um auf die Bilder zuzugreifen, überprüfen Sie zuerst, ob sie bereits vorhanden sind, wenn nicht, und laden Sie sie dann herunter

          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() ; 
            }
Nasz Njoka Sr.
quelle
Hinweis : Obwohl dieses Beispiel im Allgemeinen funktionieren könnte, bietet es keine Fehlerbehandlung und es fehlt auch ein grundlegendes Verständnis der AsyncTaskVorteile (ordnungsgemäße Verwendung der Parametrisierung, parallele Ausführung ..). Einzelheiten entnehmen Sie bitte meinen Beispielen unten. PS . Für diejenigen, die vielleicht denken, ich hätte dies geschrieben, um meinen eigenen Code aus irgendeinem Grund zu bewerben: Nein, ich weise nur auf die Probleme hin, die ich im gegebenen Beispiel sehen kann.
Droidman
Ja Droidman, ich stimme dir zu. Dieser Code sollte als Tamplate verwendet werden und muss selbst ausgefüllt werden, einschließlich der Fehlerbehandlung. Übrigens fehlt Ihrem Code auch die Fehlerbehandlung. Was ist mit Ihrer Verbindung und Ihren Streams im Falle einer IOException passiert?
Androider
@Androider Bitte schauen Sie sich meine downloadMethode genauer an , insbesondere die doInBackgroundMethode der von mir verwendeten Aufgabe. Ein IOExceptionwürde im catch (Throwable e)Block landen , was dazu führen würde , dass ein Rückruf ImageErrorzurückgegeben und der onError()Rückruf ausgelöst wird. Das ImageErrorObjekt enthält die ursprüngliche Stapelverfolgung und die Ursache des aufgetretenenException
Droidman
1
Ja, aber Ihre Verbindung wird nicht getrennt und Ihre Streams werden nicht geschlossen
Androider
@ Androider ah, ich verstehe deinen Standpunkt. Obwohl ich auf meinen Testgeräten keine verdächtigen Lecks bemerkt habe, kann dieses Verhalten auf anderen Geräten anders sein. Vielen Dank für den Hinweis - Ich habe den Code aktualisiert
Droidman
32

Warum brauchen Sie wirklich Ihren eigenen Code, um ihn herunterzuladen? Wie wäre es, wenn Sie Ihre URI einfach an den Download-Manager übergeben?

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

}
AnhSirk Dasarp
quelle
1
Perfektes Beispiel für API> 8.
Jeeten Parmar
Ihr Code funktioniert perfekt für ein Bild! Was kann ich tun, wenn ich mehr (100 Bilder) von meiner Bilddatei auf meinem Server herunterladen möchte?
Kostantinos Ibra
Ich erhalte die Nachricht, dass die Datei beschädigt ist!
Anup
2
Möglicherweise .setDestinationInExternalPublicDir ("/ AnhsirkDasarp", "fileName.jpg");
Experte will
2
Woher wissen wir, ob es erfolgreich heruntergeladen wurde oder fehlgeschlagen ist
Prabs
12

es könnte dir helfen ..

Button download_image = (Button)bigimagedialog.findViewById(R.id.btn_downloadimage);
                    download_image.setOnClickListener(new View.OnClickListener()
                    {
                        public void onClick(View v)
                        {
                            boolean success = (new File("/sdcard/dirname")).mkdir(); 
                            if (!success)
                            {
                                Log.w("directory not created", "directory not created");
                            }

                            try
                            {
                                URL url = new URL("YOUR_URL");
                                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                                connection.setDoInput(true);
                                connection.connect();
                                InputStream input = connection.getInputStream();
                                Bitmap myBitmap = BitmapFactory.decodeStream(input);

                                String data1 = String.valueOf(String.format("/sdcard/dirname/%d.jpg",System.currentTimeMillis()));

                                FileOutputStream stream = new FileOutputStream(data1);

                                ByteArrayOutputStream outstream = new ByteArrayOutputStream();
                                myBitmap.compress(Bitmap.CompressFormat.JPEG, 85, outstream);
                                byte[] byteArray = outstream.toByteArray();

                                stream.write(byteArray);
                                stream.close();

                                Toast.makeText(getApplicationContext(), "Downloading Completed", Toast.LENGTH_SHORT).show();
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }
                        }
                    });
Ajay
quelle
6

Ich habe eine einfache Lösung, die perfekt funktioniert. Der Code gehört nicht mir, ich habe ihn auf diesem Link gefunden . Hier sind die folgenden Schritte:

1. Bevor Sie das Bild herunterladen, schreiben Sie eine Methode zum Speichern der Bitmap in eine Bilddatei im internen Speicher von Android. Es benötigt einen Kontext, besser ist es, den Pass im Anwendungskontext von getApplicationContext () zu verwenden. Diese Methode kann in Ihre Activity-Klasse oder andere Util-Klassen kopiert werden.

public void saveImage(Context context, Bitmap b, String imageName) 
{
    FileOutputStream foStream;
    try 
    {
        foStream = context.openFileOutput(imageName, Context.MODE_PRIVATE);
        b.compress(Bitmap.CompressFormat.PNG, 100, foStream);
        foStream.close();
    } 
    catch (Exception e) 
    {
        Log.d("saveImage", "Exception 2, Something went wrong!");
        e.printStackTrace();
    }
}

2. Jetzt haben wir eine Methode zum Speichern der Bitmap in einer Bilddatei in andorid. Schreiben wir die AsyncTask zum Herunterladen von Bildern per URL. Diese private Klasse muss als Unterklasse in Ihre Aktivitätsklasse eingefügt werden. Nachdem das Bild heruntergeladen wurde, ruft es in der onPostExecute-Methode die oben definierte saveImage-Methode auf, um das Bild zu speichern. Beachten Sie, dass der Bildname als "my_image.png" fest codiert ist.

private class DownloadImage extends AsyncTask<String, Void, Bitmap> {
    private String TAG = "DownloadImage";
    private Bitmap downloadImageBitmap(String sUrl) {
        Bitmap bitmap = null;
        try {
            InputStream inputStream = new URL(sUrl).openStream();   // Download Image from URL
            bitmap = BitmapFactory.decodeStream(inputStream);       // Decode Bitmap
            inputStream.close();
        } catch (Exception e) {
            Log.d(TAG, "Exception 1, Something went wrong!");
            e.printStackTrace();
        }
        return bitmap;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        return downloadImageBitmap(params[0]);
    }

    protected void onPostExecute(Bitmap result) {
        saveImage(getApplicationContext(), result, "my_image.png");
    }
}

3. Die AsyncTask zum Herunterladen des Bildes ist definiert, aber wir müssen sie ausführen, um diese AsyncTask auszuführen. Schreiben Sie dazu diese Zeile in Ihre onCreate-Methode in Ihrer Activity-Klasse oder in eine onClick-Methode einer Schaltfläche oder an andere Stellen, die Sie für richtig halten.

new DownloadImage().execute("http://developer.android.com/images/activity_lifecycle.png");

Das Bild sollte in /data/data/your.app.packagename/files/my_image.jpeg gespeichert werden. Überprüfen Sie diesen Beitrag, um von Ihrem Gerät aus auf dieses Verzeichnis zuzugreifen.

IMO das löst das Problem! Wenn Sie weitere Schritte wie das Laden des Bildes wünschen, können Sie die folgenden zusätzlichen Schritte ausführen:

4. Nachdem das Image heruntergeladen wurde, benötigen wir eine Möglichkeit, die Image-Bitmap aus dem internen Speicher zu laden, damit wir sie verwenden können. Schreiben wir die Methode zum Laden der Bild-Bitmap. Diese Methode verwendet zwei Parameter, einen Kontext und einen Bilddateinamen. Ohne den vollständigen Pfad sucht context.openFileInput (imageName) die Datei im Speicherverzeichnis, wenn dieser Dateiname in der obigen saveImage-Methode gespeichert wurde.

public Bitmap loadImageBitmap(Context context, String imageName) {
    Bitmap bitmap = null;
    FileInputStream fiStream;
    try {
        fiStream    = context.openFileInput(imageName);
        bitmap      = BitmapFactory.decodeStream(fiStream);
        fiStream.close();
    } catch (Exception e) {
        Log.d("saveImage", "Exception 3, Something went wrong!");
        e.printStackTrace();
    }
    return bitmap;
}

5. Jetzt haben wir alles, was wir zum Einstellen des Bildes einer ImageView oder anderer Ansichten benötigen, für die Sie das Bild verwenden möchten. Wenn wir das Bild speichern, haben wir den Bildnamen als "my_image.jpeg" fest codiert. Jetzt können wir diesen Bildnamen an die obige loadImageBitmap-Methode übergeben, um die Bitmap abzurufen und auf eine ImageView festzulegen.

someImageView.setImageBitmap(loadImageBitmap(getApplicationContext(), "my_image.jpeg"));

6. Um den vollständigen Pfad des Bildes anhand des Bildnamens zu erhalten.

File file            = getApplicationContext().getFileStreamPath("my_image.jpeg");
String imageFullPath = file.getAbsolutePath();

7. Überprüfen Sie, ob die Bilddatei vorhanden ist.

Datei Datei =

getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
  1. So löschen Sie die Bilddatei.

    Datei file = getApplicationContext (). GetFileStreamPath ("my_image.jpeg"); if (file.delete ()) Log.d ("file", "my_image.jpeg gelöscht!");

Gláucio Leonardo Sant'ana
quelle
0

Dieser Code läuft perfekt in meinem Projekt

downloadImagesToSdCard(imagepath,imagepath);

private void downloadImagesToSdCard(String downloadUrl,String imageName)
        {
            try
            {
                URL url = new URL("www.xxx.com"+downloadUrl); 
                /* making a directory in sdcard */
            //  String sdCard=Environment.getExternalStorageDirectory().toString();
                ContextWrapper cw = new ContextWrapper(getActivity());
                 // path to /data/data/yourapp/app_data/imageDir
                File directory = cw.getDir("files", Context.MODE_PRIVATE);

                File myDir = new File(directory,"folder");

                /*  if specified not exist create new */
                if(!myDir.exists())
                {
                    myDir.mkdir();
                    Log.v("", "inside mkdir");
                }

                /* checks the file and if it already exist delete */
                String fname = imageName;
                File file = new File (myDir, fname);
                Log.d("file===========path", ""+file);
                if (file.exists ()) 
                    file.delete (); 

                /* Open a connection */
                URLConnection ucon = url.openConnection();
                InputStream inputStream = null;
                HttpURLConnection httpConn = (HttpURLConnection)ucon;
                httpConn.setRequestMethod("GET");
                httpConn.connect();
                inputStream = httpConn.getInputStream();
                /*if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) 
                {
                    inputStream = httpConn.getInputStream();
                }*/

                FileOutputStream fos = new FileOutputStream(file);  
                int totalSize = httpConn.getContentLength();
                int downloadedSize = 0;   
                byte[] buffer = new byte[1024];
                int bufferLength = 0;
                while ( (bufferLength = inputStream.read(buffer)) >0 ) 
                {                 
                    fos.write(buffer, 0, bufferLength);                  
                    downloadedSize += bufferLength;                 
                    Log.i("Progress:","downloadedSize:"+downloadedSize+"totalSize:"+ totalSize) ;
                }   

                fos.close();
                Log.d("test", "Image Saved in sdcard..");  
                viewimage();
            }
            catch(IOException io)
            {                  
                io.printStackTrace();
            }
            catch(Exception e)
            {                     
                e.printStackTrace();
            }


        } 

        public void viewimage()
        {
              String path = serialnumber+".png";
               ContextWrapper cw = new ContextWrapper(getActivity());

                //path to /data/data/yourapp/app_data/dirName
                File directory = cw.getDir("files", Context.MODE_PRIVATE);

                File mypath=new File(directory,"folder/"+path);

                Bitmap b;
                try {
                    b = BitmapFactory.decodeStream(new FileInputStream(mypath));
                //  b.compress(format, quality, stream)
                    profile_image.setImageBitmap(Bitmap.createScaledBitmap(b, 120, 120, false));
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }
tej shah
quelle
1
Hallo, danke für den Beitrag. Beachten Sie, dass der oben beschriebene Ansatz zum Speichern des Bilds im privaten Speicher der App zwei Nachteile hat: 1) Das Bild steht nur Ihrer App zur Verfügung und 2) Sie sollten das Speichern von Bildern im privaten Speicher der App vermeiden, da es nicht für große Inhalte vorgesehen ist.
Droidman
0

Versuche dies

 try
                {
                    Bitmap bmp = null;
                    URL url = new URL("Your_URL");
                    URLConnection conn = url.openConnection();
                    bmp = BitmapFactory.decodeStream(conn.getInputStream());
                    File f = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis() + ".jpg");
                    if(f.exists())
                        f.delete();
                    f.createNewFile();
                    Bitmap bitmap = bmp;
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
                    byte[] bitmapdata = bos.toByteArray();
                    FileOutputStream fos = new FileOutputStream(f);
                    fos.write(bitmapdata);
                    fos.flush();
                    fos.close();
                    Log.e(TAG, "imagepath: "+f );
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
Sunil
quelle
0
public class testCrop extends AppCompatActivity {
    ImageView iv;
    String imagePath = "https://style.pk/wp-content/uploads/2015/07/omer-Shahzad-performed-umrah-600x548.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.testcrpop);
        iv = (ImageView) findViewById(R.id.testCrop);
        imageDownload image = new imageDownload(testCrop.this, iv);
        image.execute(imagePath);
    }
    class imageDownload extends AsyncTask<String, Integer, Bitmap> {
        Context context;
        ImageView imageView;
        Bitmap bitmap;
        InputStream in = null;
        int responseCode = -1;
//constructor.
        public imageDownload(Context context, ImageView imageView) {
            this.context = context;
            this.imageView = imageView;
        }
        @Override
        protected void onPreExecute() {
        }
        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                URL url = new URL(params[0]);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setDoOutput(true);
                httpURLConnection.connect();
                responseCode = httpURLConnection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    in = httpURLConnection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(in);
                    in.close();
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bitmap;
        }
        @Override
        protected void onPostExecute(Bitmap data) {
            imageView.setImageBitmap(data);
            saveImage(data);
        }

        private void saveImage(Bitmap data) {
            File createFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"test");
            createFolder.mkdir();
            File saveImage = new File(createFolder,"downloadimage.jpg");
            try {
                OutputStream outputStream = new FileOutputStream(saveImage);
                data.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
                outputStream.flush();
                outputStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

AUSGABEGeben Sie hier die Bildbeschreibung ein

Stellen Sie sicher, dass Sie die Berechtigung zum Schreiben von Daten in den Speicher hinzugefügt haben

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Shahzad Afridi
quelle