Comment télécharger et enregistrer une image sur Android

Réponses:

224

Modifier au 30.12.2015 - Le guide ultime du téléchargement d'images


dernière mise à jour majeure: 31 mars 2016


TL; DR aka arrête de parler, donne-moi juste le code !!

Passez au bas de cet article, copiez la BasicImageDownloader(version javadoc ici ) dans votre projet, implémentez l' OnImageLoaderListenerinterface et vous avez terminé.

Remarque : bien que le BasicImageDownloaderlogiciel gère les erreurs possibles et empêche votre application de planter en cas de problème, il n'effectuera aucun post-traitement (par exemple, une réduction de la taille) sur le fichier téléchargé Bitmaps.


Étant donné que cet article a reçu beaucoup d'attention, j'ai décidé de le retravailler complètement pour empêcher les gens d'utiliser des technologies obsolètes, de mauvaises pratiques de programmation ou simplement de faire des choses stupides - comme chercher des «hacks» pour exécuter le réseau sur le fil principal ou accepter tous les certificats SSL.

J'ai créé un projet de démonstration nommé "Image Downloader" qui montre comment télécharger (et enregistrer) une image en utilisant ma propre implémentation de téléchargeur, les DownloadManagerbibliothèques intégrées d'Android ainsi que certaines bibliothèques open-source populaires. Vous pouvez afficher le code source complet ou télécharger le projet sur GitHub .

Remarque : je n'ai pas encore ajusté la gestion des autorisations pour le SDK 23+ (Marshmallow), donc le projet cible le SDK 22 (Lollipop).

Dans ma conclusion à la fin de cet article, je partagerai mon humble avis sur le cas d'utilisation approprié pour chaque mode particulier de téléchargement d'image que j'ai mentionné.

Commençons par une propre implémentation (vous pouvez trouver le code à la fin de l'article). Tout d'abord, c'est un ImageDownloader de base et c'est tout. Tout ce qu'il fait, c'est se connecter à l'url donnée, lire les données et essayer de les décoder en tant que Bitmap, déclenchant les OnImageLoaderListenerrappels d'interface le cas échéant. L'avantage de cette approche - c'est simple et vous avez une vue d'ensemble claire de ce qui se passe. Une bonne façon de procéder si tout ce dont vous avez besoin est de télécharger / afficher et enregistrer certaines images, sans que vous vous souciez de maintenir un cache mémoire / disque.

Remarque: en cas d'images volumineuses, vous devrez peut-être les réduire .

-

Android DownloadManager est un moyen de laisser le système gérer le téléchargement pour vous. Il est en fait capable de télécharger tout type de fichiers, pas seulement des images. Vous pouvez laisser votre téléchargement se faire en silence et invisible pour l'utilisateur, ou vous pouvez permettre à l'utilisateur de voir le téléchargement dans la zone de notification. Vous pouvez également vous inscrire BroadcastReceiverpour être averti une fois le téléchargement terminé. La configuration est assez simple, reportez-vous au projet lié pour un exemple de code.

L'utilisation de DownloadManagern'est généralement pas une bonne idée si vous souhaitez également afficher l'image, car vous devez lire et décoder le fichier enregistré au lieu de simplement définir le fichier téléchargé Bitmapdans un fichier ImageView. Le DownloadManagerne fournit pas non plus d'API pour votre application pour suivre la progression du téléchargement.

-

Maintenant, l'introduction de la grande substance - les bibliothèques. Ils peuvent faire bien plus que simplement télécharger et afficher des images, notamment: créer et gérer la mémoire cache / disque, redimensionner les images, les transformer et plus encore.

Je commencerai par Volley , une puissante bibliothèque créée par Google et couverte par la documentation officielle. Tout en étant une bibliothèque de mise en réseau polyvalente non spécialisée sur les images, Volley dispose d'une API assez puissante pour gérer les images.

Vous devrez implémenter une classe Singleton pour gérer les requêtes Volley et vous êtes prêt à partir.

Vous voudrez peut-être remplacer le vôtre ImageViewpar Volley NetworkImageView, de sorte que le téléchargement devient essentiellement une ligne unique:

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

Si vous avez besoin de plus de contrôle, voici à quoi cela ressemble de créer un ImageRequestavec 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
                }
            });

Il est à noter que Volley dispose d'un excellent mécanisme de gestion des erreurs en fournissant la VolleyErrorclasse qui vous aide à déterminer la cause exacte d'une erreur. Si votre application fait beaucoup de réseautage et que la gestion d'images n'est pas son objectif principal, Volley est la solution idéale pour vous.

-

Picasso de Square est une bibliothèque bien connue qui s'occupe de tout le chargement d'images pour vous. Le simple fait d'afficher une image à l'aide de Picasso est aussi simple que:

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

Par défaut, Picasso gère le cache disque / mémoire, vous n'avez donc pas à vous en soucier. Pour plus de contrôle, vous pouvez implémenter l' Targetinterface et l'utiliser pour charger votre image - cela fournira des rappels similaires à l'exemple de Volley. Consultez le projet de démonstration pour des exemples.

Picasso vous permet également d'appliquer des transformations à l'image téléchargée et il existe même d' autres bibliothèques qui étendent ces API. Fonctionne également très bien dans un RecyclerView/ ListView/ GridView.

-

Universal Image Loader est une autre bibliothèque très populaire servant à la gestion des images. Il utilise le sien ImageLoaderqui (une fois initialisé) a une instance globale qui peut être utilisée pour télécharger des images dans une seule ligne de code:

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

Si vous souhaitez suivre la progression du téléchargement ou accéder au téléchargement 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
                }
            });

L' optsargument de cet exemple est un DisplayImageOptionsobjet. Reportez-vous au projet de démonstration pour en savoir plus.

Semblable à Volley, UIL fournit la FailReasonclasse qui vous permet de vérifier ce qui n'a pas fonctionné en cas d'échec du téléchargement. Par défaut, UIL maintient un cache mémoire / disque si vous ne lui dites pas explicitement de ne pas le faire.

Remarque : l'auteur a mentionné qu'il ne maintenait plus le projet à partir du 27 novembre 2015. Mais comme il y a beaucoup de contributeurs, nous pouvons espérer que l'Universal Image Loader continuera à vivre.

-

Fresco de Facebook est la bibliothèque la plus récente et (IMO) la plus avancée qui porte la gestion des images à un nouveau niveau: de Bitmapsla suppression du tas Java (avant Lollipop) à la prise en charge des formats animés et du streaming JPEG progressif .

Pour en savoir plus sur les idées et les techniques derrière Fresco, reportez-vous à cet article .

L'utilisation de base est assez simple. Notez que vous n'aurez besoin d'appeler Fresco.initialize(Context);qu'une seule fois, de préférence dans la Applicationclasse. L'initialisation de Fresco plusieurs fois peut entraîner un comportement imprévisible et des erreurs MOO.

Fresco utilise Drawees pour afficher des images, vous pouvez les considérer comme des 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" />

Comme vous pouvez le voir, beaucoup de choses (y compris les options de transformation) sont déjà définies en XML, donc tout ce que vous avez à faire pour afficher une image est une ligne unique:

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

Fresco fournit une API de personnalisation étendue, qui, dans certaines circonstances, peut être assez complexe et oblige l'utilisateur à lire attentivement la documentation (oui, parfois vous avez besoin de RTFM).

J'ai inclus des exemples de JPEG progressif et d'images animées dans l'exemple de projet.


Conclusion - "J'ai appris les bonnes choses, que dois-je utiliser maintenant?"

Notez que le texte suivant reflète mon opinion personnelle et ne doit pas être considéré comme un postulat.

  • Si vous avez seulement besoin de télécharger / enregistrer / afficher certaines images, ne prévoyez pas de les utiliser dans un Recycler-/Grid-/ListViewet n'avez pas besoin de tout un tas d'images pour être prêtes pour l'affichage, le BasicImageDownloader devrait répondre à vos besoins.
  • Si votre application enregistre des images (ou d'autres fichiers) à la suite d'un utilisateur ou d'une action automatisée et que vous n'avez pas besoin que les images s'affichent souvent, utilisez Android DownloadManager .
  • Si votre application fait beaucoup de réseautage, transmet / reçoit des JSONdonnées, fonctionne avec des images, mais que ce ne sont pas l'objectif principal de l'application, optez pour Volley .
  • Votre application est axée sur l'image / le média, vous souhaitez appliquer certaines transformations aux images et ne voulez pas vous soucier des API complexes: utilisez Picasso (Remarque: ne fournit aucune API pour suivre l'état de téléchargement intermédiaire) ou Universal Image Chargeur
  • Si votre application concerne uniquement les images, vous avez besoin de fonctionnalités avancées telles que l'affichage de formats animés et vous êtes prêt à lire la documentation, utilisez Fresco .

Au cas où vous auriez manqué cela, le lien Github pour le projet de démonstration.


Et voici le 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
la source
Qu'en est-il du rappel onPictureTaken () qui donne l'image sous forme d'octet [], peut-on obtenir une URL vers cette image, directement depuis la caméra? Ou est-ce que l'ancien outputStream () de base est le seul moyen dans Android de sauvegarder une photo qui a été prise par un appareil photo sans utiliser l'intention intégrée? Cela semble étrange, car la chose naturelle à faire après onPictureTaken est bien sûr de le sauvegarder. N'y at-il pas de soutien particulier pour faire cela?
Tombola du
2
@Tombola Salut! Cet article concerne le téléchargement d'une image sur le Web. Mais pour répondre à votre question (pour autant que je l'ai compris): la manière courante de sauvegarder une image de caméra consiste à obtenir son chemin depuis le Cursor(dans la onActivityResult()méthode), puis à créer un en Bitmaputilisant ce chemin. Et oui, vous n'échapperez pas à l'utilisation de a FileOutputStreamet a ByteArrayOutputStreamsi vous souhaitez enregistrer cette image en SD.
Droidman
@BartBurg cette question concerne le téléchargement et l'enregistrement d'une image. Mais vous avez raison à un moment donné, car il existe une méthode d'écriture, il devrait également y avoir une méthode de lecture par souci d'exhaustivité. Je l'ajouterai bientôt dans la prochaine mise à jour de ce post.
Droidman
Pouvez-vous s'il vous plaît fournir un exemple en utilisant ce BasicImageDownloader?
Jaydev
1
@JaydevKalivarapu s'il vous plaît vérifier l'application de démonstration sur GitHub ( classe source contenant l'exemple )
Droidman
35

Je viens de résoudre ce problème et je voudrais partager le code complet qui peut télécharger, enregistrer sur la carte SD (et masquer le nom de fichier) et récupérer les images et enfin il vérifie si l'image est déjà là. L'URL provient de la base de données, de sorte que le nom de fichier peut être facilement unique en utilisant id.

télécharger d'abord les images

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

Ensuite, créez une classe pour enregistrer et récupérer les fichiers

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

Ensuite, pour accéder aux images, vérifiez d'abord si elle est déjà là sinon puis téléchargez

          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.
la source
Remarque : bien que cet exemple puisse généralement fonctionner, il ne fournit aucune gestion des erreurs et manque également de connaissances de base sur AsyncTaskles avantages de 's (bonne utilisation du paramétrage, exécution parallèle ..). Veuillez consulter mes exemples ci-dessous pour plus de détails. PS . pour ceux qui pourraient penser que j'ai écrit ceci pour promouvoir mon propre code pour une raison quelconque: non, je signale simplement les problèmes que je peux voir dans l'exemple donné.
Droidman
Oui Droidman, je suis d'accord avec vous. Ce morceau de code doit être pris comme un tamplate et il faut le compléter seul, y compris la gestion des erreurs. Au fait, votre code manque également de gestion des erreurs. Qu'arrivera-t-il à votre connexion et à vos flux en cas d'IOException?
Androider
@Androider veuillez regarder de plus près ma downloadméthode, en particulier la doInBackgroundméthode de la tâche que j'utilise. An IOExceptionatterrirait dans le catch (Throwable e)bloc, ce qui entraînerait un ImageErrorretour et le onError()déclenchement du rappel. L' ImageErrorobjet contiendra la trace de la pile d'origine et la cause du Exception
problème
1
Oui, mais votre connexion ne sera pas déconnectée et vos streams ne seront pas fermés
Androider
@Androider ah, je comprends votre point de vue. Bien que je n'ai jamais remarqué de fuites suspectes sur mes appareils de test, ce comportement peut être différent sur d'autres appareils. Merci pour l'indice - J'ai mis à jour le code
Droidman
32

Pourquoi avez-vous vraiment besoin de votre propre code pour le télécharger? Que diriez-vous simplement de passer votre URI au gestionnaire de téléchargement?

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
la source
1
Exemple parfait pour api> 8.
Jeeten Parmar
Votre code fonctionne parfaitement pour une image! que puis-je faire si je veux télécharger plus (100 images) de mon fichier d'images sur mon serveur ???
Kostantinos Ibra
Je reçois un message indiquant que le fichier est endommagé!
Anup
2
Peut-être, .setDestinationInExternalPublicDir ("/ AnhsirkDasarp", "fileName.jpg");
expert veut être
2
Comment savoir s'il a été téléchargé avec succès ou échec
Prabs
12

cela pourrait vous aider.

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
la source
6

J'ai une solution simple qui fonctionne parfaitement. Le code n'est pas le mien, je l'ai trouvé sur ce lien . Voici les étapes à suivre:

1. Avant de télécharger l'image, écrivons une méthode pour enregistrer le bitmap dans un fichier image dans la mémoire interne d'Android. Il faut un contexte, mieux utiliser le pass dans le contexte de l'application par getApplicationContext (). Cette méthode peut être sauvegardée dans votre classe Activity ou dans d'autres classes util.

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. Maintenant, nous avons une méthode pour enregistrer le bitmap dans un fichier image en andorid, écrivons l'AsyncTask pour télécharger des images par url. Cette classe privée doit être placée dans votre classe d'activité en tant que sous-classe. Une fois l'image téléchargée, dans la méthode onPostExecute, il appelle la méthode saveImage définie ci-dessus pour enregistrer l'image. Notez que le nom de l'image est codé en dur sous la forme «my_image.png».

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. La AsyncTask pour télécharger l'image est définie, mais nous devons l'exécuter pour exécuter cette AsyncTask. Pour ce faire, écrivez cette ligne dans votre méthode onCreate dans votre classe Activity, ou dans une méthode onClick d'un bouton ou à d'autres endroits qui vous conviennent.

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

L'image doit être enregistrée dans /data/data/your.app.packagename/files/my_image.jpeg, vérifiez ce post pour accéder à ce répertoire depuis votre appareil.

IMO cela résout le problème! Si vous souhaitez d'autres étapes telles que charger l'image, vous pouvez suivre ces étapes supplémentaires:

4. Une fois l'image téléchargée, nous avons besoin d'un moyen de charger l'image bitmap à partir du stockage interne, afin que nous puissions l'utiliser. Écrivons la méthode de chargement de l'image bitmap. Cette méthode prend deux paramètres, un contexte et un nom de fichier image, sans le chemin complet, le context.openFileInput (imageName) recherchera le fichier dans le répertoire de sauvegarde lorsque ce nom de fichier a été enregistré dans la méthode saveImage ci-dessus.

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. Nous avons maintenant tout ce dont nous avons besoin pour définir l'image d'un ImageView ou de toute autre vue sur laquelle vous souhaitez utiliser l'image. Lorsque nous enregistrons l'image, nous avons codé en dur le nom de l'image en tant que «my_image.jpeg», maintenant nous pouvons passer ce nom d'image à la méthode loadImageBitmap ci-dessus pour obtenir le bitmap et le définir sur ImageView.

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

6. Pour obtenir le chemin complet de l'image par nom d'image.

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

7. Vérifiez si le fichier image existe.

Fichier fichier =

getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
  1. Pour supprimer le fichier image.

    Fichier fichier = getApplicationContext (). GetFileStreamPath ("mon_image.jpeg"); if (file.delete ()) Log.d ("fichier", "mon_image.jpeg supprimé!");

Gláucio Leonardo Sant'ana
la source
0

ce code fonctionne parfaitement dans mon projet

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
la source
1
salut, merci pour la contribution. Notez que l'approche ci-dessus consistant à enregistrer l'image sur le stockage privé de l'application présente 2 inconvénients: 1) l'image ne sera disponible que pour votre application et 2) vous devriez éviter d'enregistrer les images sur le stockage privé de l'application car elle n'est pas destinée au contenu volumineux.
Droidman
0

Essaye ça

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

PRODUCTIONentrez la description de l'image ici

Assurez-vous d'avoir ajouté l'autorisation d'écrire des données en mémoire

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