Как загрузить и сохранить изображение на Android

Как загрузить и сохранить изображение с заданного URL-адреса в Android?


person Droidman    schedule 21.03.2013    source источник


Ответы (10)


Редактировать от 30.12.2015 - Полное руководство по загрузке изображений


последнее крупное обновление: 31 марта 2016 г.


TL;DR, иначе говоря, хватит болтать, просто дайте мне код!!

Перейдите к нижней части этого сообщения, скопируйте BasicImageDownloader (версия javadoc здесь) в свой проект, реализуйте интерфейс OnImageLoaderListener, и все готово.

Примечание: хотя BasicImageDownloader обрабатывает возможные ошибки и предотвращает сбой вашего приложения в случае, если что-то пойдет не так, он не будет выполнять какую-либо постобработку (например, уменьшение размера) загруженного Bitmaps.


Поскольку этот пост получил довольно много внимания, я решил полностью переработать его, чтобы люди не использовали устаревшие технологии, плохие методы программирования или просто делали глупости — например, искали «хаки» для запуска сети в основном потоке или принимать все сертификаты SSL.

Я создал демонстрационный проект под названием «Загрузчик изображений», который демонстрирует, как загружать (и сохранять) изображение, используя мою собственную реализацию загрузчика, встроенный в Android DownloadManager, а также некоторые популярные библиотеки с открытым исходным кодом. Вы можете просмотреть полный исходный код или загрузить проект на GitHub.

Примечание. Я еще не настроил управление разрешениями для SDK 23+ (Marshmallow), поэтому проект ориентирован на SDK 22 (Lollipop).

В моем заключении в конце этого поста я поделюсь своим скромным мнением о правильном использовании каждого конкретного способа загрузки изображений. упомянул.

Начнем с собственной реализации (код вы найдете в конце поста). Прежде всего, это BasicImageDownloader и этим все сказано. Все, что он делает, это подключается к заданному URL-адресу, считывает данные и пытается декодировать их как Bitmap, вызывая обратные вызовы интерфейса OnImageLoaderListener, когда это необходимо. Преимущество такого подхода — он прост и у вас есть четкое представление о том, что происходит. Хороший способ, если все, что вам нужно, это загрузка/отображение и сохранение некоторых изображений, в то время как вам не нужно поддерживать кеш памяти/диска.

Примечание. В случае больших изображений может потребоваться уменьшить их масштаб.

--

Android DownloadManager — это способ, позволяющий системе выполнять загрузку за вас. На самом деле он способен загружать любые файлы, а не только изображения. Вы можете сделать так, чтобы ваша загрузка происходила тихо и незаметно для пользователя, или вы можете разрешить пользователю видеть загрузку в области уведомлений. Вы также можете зарегистрировать BroadcastReceiver, чтобы получать уведомления после завершения загрузки. Настройка довольно проста, см. связанный проект для примера кода.

Использование DownloadManager, как правило, не является хорошей идеей, если вы также хотите отобразить изображение, поскольку вам нужно будет прочитать и декодировать сохраненный файл, а не просто установить загруженный Bitmap в ImageView. DownloadManager также не предоставляет вашему приложению никакого API для отслеживания хода загрузки.

--

Теперь знакомство с замечательными вещами — библиотеками. Они могут делать гораздо больше, чем просто загружать и отображать изображения, в том числе: создавать и управлять кэшем памяти/диска, изменять размер изображений, преобразовывать их и многое другое.

Я начну с Volley, мощной библиотеки, созданной Google и подпадающей под действие официального документация. Будучи сетевой библиотекой общего назначения, не специализирующейся на изображениях, Volley имеет довольно мощный API для управления изображениями.

Вам нужно будет реализовать класс Singleton для управления запросами Volley, и все готово.

Возможно, вы захотите заменить свой ImageView на NetworkImageView Volley, чтобы загрузка в основном стала однострочной:

((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 идеально вам подойдет.

--

Picasso от Square – это известная библиотека, которая сделает за вас всю загрузку изображений. Просто отобразить изображение с помощью Picasso так же просто, как:

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

По умолчанию Picasso управляет кешем диска/памяти, поэтому вам не нужно об этом беспокоиться. Для большего контроля вы можете реализовать интерфейс Target и использовать его для загрузки вашего изображения — это обеспечит обратные вызовы, аналогичные примеру Volley. Проверьте демонстрационный проект для примеров.

Picasso также позволяет применять преобразования к загруженному изображению, и даже существуют другие библиотеки, расширяющие эти API. . Также очень хорошо работает в RecyclerView/ListView/GridView.

--

Universal Image Loader — еще одна очень популярная библиотека, предназначенная для управления изображениями. Он использует собственный ImageLoader, который (после инициализации) имеет глобальный экземпляр, который можно использовать для загрузки изображений в одной строке кода:

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

Если вы хотите отслеживать ход загрузки или получить доступ к загруженному файлу Bitmap:

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

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

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

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

Аргумент opts в этом примере является объектом DisplayImageOptions. Обратитесь к демонстрационному проекту, чтобы узнать больше.

Подобно Volley, UIL предоставляет класс FailReason, который позволяет вам проверить, что пошло не так при сбое загрузки. По умолчанию UIL поддерживает кэш памяти/диска, если вы явно не укажете ему не делать этого.

Примечание: автор упомянул, что он больше не поддерживает проект с 27 ноября 2015 года. Но поскольку участников много, мы можем надеяться, что универсальный загрузчик изображений будет жить.

--

Fresco от Facebook — это новейшая и (IMO) самая продвинутая библиотека, которая выводит управление изображениями на новый уровень: от отключения Bitmaps кучу java (до Lollipop) для поддержки анимированных форматов и прогрессивная потоковая передача JPEG.

Чтобы узнать больше об идеях и методах, лежащих в основе Fresco, см. этот пост.

Основное использование довольно простое. Обратите внимание, что вам нужно будет вызвать Fresco.initialize(Context); только один раз, предпочтительно в классе Application. Инициализация Fresco более одного раза может привести к непредсказуемому поведению и ошибкам OOM.

Fresco использует Drawees для отображения изображений, вы можете думать о них как о ImageViews:

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

Как видите, многие вещи (включая параметры преобразования) уже определены в XML, поэтому все, что вам нужно сделать, чтобы отобразить изображение, — это однострочный код:

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

Fresco предоставляет расширенный API настройки, который в определенных обстоятельствах может быть довольно сложным и требует от пользователя внимательного прочтения документации (да, иногда нужно RTFM).

Я включил примеры для прогрессивного JPEG и анимированных изображений в пример проекта.


Вывод - "Я узнал об отличном материале, что мне теперь использовать?"

Обратите внимание, что следующий текст отражает мое личное мнение и не должен восприниматься как постулат.

  • Если вам нужно только загрузить/сохранить/отобразить некоторые изображения, не планируйте использовать их в Recycler-/Grid-/ListView и не нужно, чтобы вся куча изображений была готова к отображению, вам подойдет BasicImageDownloader. твои нужды.
  • Если ваше приложение сохраняет изображения (или другие файлы) в результате действий пользователя или автоматических действий и вам не нужно, чтобы изображения отображались часто, используйте Android DownloadManager.
  • Если ваше приложение много взаимодействует с сетью, передает/получает JSON данных, работает с изображениями, но это не является основной целью приложения, используйте Volley.
  • Ваше приложение ориентировано на изображения/медиафайлы, вы хотите применить некоторые преобразования к изображениям и не хотите возиться со сложным API: используйте Picasso (Примечание: не предоставляет никакого API для отслеживания промежуточный статус загрузки) или Универсальный загрузчик изображений
  • Если ваше приложение полностью связано с изображениями, вам нужны расширенные функции, такие как отображение анимированных форматов, и вы готовы читать документацию, используйте 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;
        }
      }
   }
person Droidman    schedule 21.03.2013
comment
Как насчет обратного вызова onPictureTaken(), который возвращает изображение в виде byte[], можно ли получить URL-адрес этого изображения прямо с камеры? Или базовый старый outputStream() единственный способ в Android сохранить изображение, сделанное камерой, без использования встроенного намерения? Это кажется странным, потому что после onPictureTaken естественно сохранить его. Нет ли особой поддержки для этого? - person Tombola; 27.12.2013
comment
@Tombola Привет! Этот пост посвящен загрузке изображения из Интернета. Но чтобы ответить на ваш вопрос (насколько я его понял): общий способ сохранения изображения с камеры - получить его путь из Cursor (в методе onActivityResult()), а затем создать Bitmap с использованием этого пути. И да, вам не избежать использования FileOutputStream и ByteArrayOutputStream, если вы хотите сохранить это изображение на SD. - person Droidman; 27.12.2013
comment
@BartBurg этот вопрос касается загрузки и сохранения изображения. Но в какой-то момент вы правы, поскольку есть метод записи, для полноты картины должен быть и метод чтения. Я добавлю его в следующем обновлении к этому сообщению в ближайшее время. - person Droidman; 26.02.2016
comment
Не могли бы вы привести пример использования этого BasicImageDownloader? - person Jaydev; 11.05.2016
comment
@JaydevKalivarapu, пожалуйста, проверьте демо-приложение на GitHub (исходный класс, содержащий пример) - person Droidman; 11.05.2016
comment
Вот как базовый выглядит на Android. Ура. - person milosmns; 11.08.2016
comment
@lukecross Я абсолютно согласен, что в настоящее время вам не следует делать что-то подобное. У нас есть множество загрузчиков изображений, у нас есть загрузчики файлов, RxJava, сопрограммы Kotlin... в основном этот класс является ответом на то, как мне загрузить и отобразить изображение с помощью простой Java без каких-либо фреймворков, и он предназначен исключительно для образовательных целей. - person Droidman; 07.08.2020

Я только что решил эту проблему, и я хотел бы поделиться полным кодом, который может загружать, сохранять на SD-карту (и скрывать имя файла) и извлекать изображения, и, наконец, он проверяет, есть ли изображение. URL-адрес исходит из базы данных, поэтому имя файла может быть легко уникальным с использованием идентификатора.

первая загрузка изображений

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

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

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

Затем создайте класс для сохранения и извлечения файлов.

  public class ImageStorage {


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

    String stored = null;

    File sdcard = Environment.getExternalStorageDirectory() ; 

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

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

public static File getImage(String imagename) {

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

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

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

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

Затем, чтобы получить доступ к изображениям, сначала проверьте, есть ли они уже там, если нет, то загрузите

          if(ImageStorage.checkifImageExists(imagename))
            {
                File file = ImageStorage.getImage("/"+imagename+".jpg"); 
                String path = file.getAbsolutePath(); 
                if (path != null){
                    b = BitmapFactory.decodeFile(path);
                    imageView.setImageBitmap(b);
                }  
            } else { 
                new GetImages(imgurl, imageView, imagename).execute() ; 
            }
person Nasz Njoka Sr.    schedule 24.04.2014
comment
Примечание: хотя этот пример в целом мог бы работать, он не обеспечивает никакой обработки ошибок, а также не имеет базового понимания преимуществ AsyncTask (правильное использование параметризации, параллельное выполнение...). Пожалуйста, обратитесь к моим примерам ниже для деталей. P.S. для тех, кто может подумать, что я написал это для продвижения своего собственного кода по какой-либо причине: нет, я просто указываю на проблемы, которые я вижу в данном примере. - person Droidman; 04.02.2016
comment
Да, Дроидман, я с тобой согласен. Этот кусок кода следует воспринимать как шаблон и дорабатывать его самостоятельно, включая обработку ошибок. Кстати, в вашем коде тоже отсутствует обработка ошибок. Что произойдет с вашим соединением и потоками в случае IOException? - person Androider; 04.02.2016
comment
@Androider, пожалуйста, внимательно посмотрите на мой метод download, особенно на метод doInBackground задачи, которую я использую. IOException попадет в блок catch (Throwable e), что приведет к возврату ImageError и запуску обратного вызова onError(). Объект ImageError будет содержать исходную трассировку стека и причину произошедшего Exception - person Droidman; 04.02.2016
comment
Да, но ваше соединение не будет разорвано и ваши потоки не будут закрыты - person Androider; 04.02.2016
comment
@Androider, а, я понимаю твою точку зрения. Хотя я никогда не замечал никаких подозрительных утечек на своих тестовых устройствах, на других устройствах такое поведение может быть другим. Спасибо за подсказку - я обновил код - person Droidman; 04.02.2016
comment
в любом случае, эти методы больше не используются, можно использовать залп для упрощения процесса загрузки изображений. - person Nasz Njoka Sr.; 05.02.2016

Зачем вам действительно нужен собственный код для его загрузки? Как насчет того, чтобы просто передать свой URI диспетчеру загрузок?

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

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

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

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

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

    mgr.enqueue(request);

}
person AnhSirk Dasarp    schedule 22.01.2014
comment
Прекрасный пример для API›8. - person Jeeten Parmar; 06.09.2014
comment
Ваш код идеально подходит для одного изображения! что я могу сделать, если я хочу загрузить больше (100 рис.) из моего файла изображений на моем сервере??? - person Kostantinos Ibra; 12.04.2016
comment
Я получаю сообщение о том, что файл поврежден! - person Anup; 24.06.2016
comment
Возможно, .setDestinationInExternalPublicDir(/AnhsirkDasarp, fileName.jpg); - person Expert wanna be; 10.11.2017
comment
Как мы узнаем, успешно ли он загружен или нет - person Prabs; 20.06.2018
comment
Этот кусок кода работает хорошо, но единственный способ проверить, загружен ли файл, - это проверить его из файлов, как сообщить об успешной или неудачной загрузке? как спросил @Prabs. - person KhalodaRK84; 19.12.2019
comment
Я получаю эту ошибку, когда пытаюсь загрузить два-три изображения одно за другим -> Утечка объекта SQLiteConnection для базы данных '..../Database'! Пожалуйста, исправьте ваше приложение, чтобы корректно завершать текущие транзакции и закрывать базу данных, когда она больше не нужна. - person Makarand; 26.12.2020

может тебе поможет..

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();
                            }
                        }
                    });
person Ajay    schedule 21.03.2013

У меня есть простое решение, которое отлично работает. Код не мой, я нашел его по этой ссылке. Вот шаги, которые необходимо выполнить:

1. Прежде чем загружать изображение, давайте напишем метод сохранения растрового изображения в файл изображения во внутренней памяти Android. Ему нужен контекст, лучше использовать проход в контексте приложения с помощью getApplicationContext(). Этот метод можно сбросить в ваш класс Activity или другие служебные классы.

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. Теперь у нас есть способ сохранить растровое изображение в файл изображения в andorid, давайте напишем AsyncTask для загрузки изображений по URL-адресу. Этот частный класс необходимо поместить в ваш класс Activity в качестве подкласса. После загрузки изображения в методе onPostExecute вызывается метод saveImage, определенный выше, для сохранения изображения. Обратите внимание, имя изображения жестко запрограммировано как «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. Определена AsyncTask для загрузки образа, но нам нужно выполнить ее, чтобы запустить эту AsyncTask. Для этого напишите эту строку в своем методе onCreate в своем классе Activity или в методе onClick кнопки или в других местах, которые вы считаете нужными.

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

Изображение должно быть сохранено в /data/data/your.app.packagename/files/my_image.jpeg, проверьте этот пост для доступа к этому каталогу с вашего устройства.

ИМХО, это решает проблему! Если вам нужны дополнительные шаги, такие как загрузка изображения, вы можете выполнить следующие дополнительные действия:

4. После загрузки изображения нам нужен способ загрузить растровое изображение изображения из внутреннего хранилища, чтобы мы могли его использовать. Давайте напишем метод для загрузки растрового изображения изображения. Этот метод принимает два параметра, контекст и имя файла изображения, без полного пути, context.openFileInput(imageName) будет искать файл в каталоге сохранения, когда это имя файла было сохранено в приведенном выше методе saveImage.

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. Теперь у нас есть все необходимое для установки изображения ImageView или любых других представлений, в которых вы хотите использовать изображение. Когда мы сохраняем изображение, мы жестко закодировали имя изображения как «my_image.jpeg», теперь мы можем передать это имя изображения в описанный выше метод loadImageBitmap, чтобы получить растровое изображение и установить его в ImageView.

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

<сильный>6. Чтобы получить полный путь к изображению по имени изображения.

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

7. Проверьте, существует ли файл изображения.

Файл файл =

getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
  1. Чтобы удалить файл изображения.

    Файл файл = getApplicationContext().getFileStreamPath("my_image.jpeg"); if (file.delete()) Log.d("file", "my_image.jpeg удален!");

person Gláucio Leonardo Sant'ana    schedule 27.06.2017

этот код отлично работает в моем проекте

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();
                }
        }
person tej shah    schedule 25.07.2015
comment
привет, спасибо за вклад. Обратите внимание, что описанный выше подход к сохранению изображения в личном хранилище приложения имеет 2 недостатка: 1) изображение будет доступно только вашему приложению и 2) вам следует избегать сохранения изображений в личном хранилище приложения, поскольку оно не предназначено для большого контента. - person Droidman; 26.07.2015

Попробуй это

 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();
                }
person Sunil    schedule 14.06.2017

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

ВЫВОД введите здесь описание изображения

Убедитесь, что вы добавили разрешение на запись данных в память

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
person Shahzad Afridi    schedule 28.09.2017

Пост @Droidman довольно всеобъемлющий. Volley хорошо работает с небольшими данными в несколько килобайт. Когда я попытался использовать «BasicImageDownloader.java», Android Studio предупредила меня, что класс AsyncTask должен быть статическим, иначе могут быть утечки. Я использовал Volley в другом тестовом приложении, и оно продолжало падать из-за утечек, поэтому я беспокоюсь об использовании Volley для загрузчика изображений (изображения могут быть меньше 100 КБ).

Я использовал Пикассо, и он работал хорошо, есть небольшое изменение (вероятно, обновление Пикассо) по сравнению с тем, что было опубликовано выше. Ниже код работал для меня:

    public static void imageDownload(Context ctx, String url){
    Picasso.get().load(yourURL)
            .into(getTarget(url));
}
private static Target getTarget(final String url){

    Target target2 = new Target() {
        @Override
        public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
            new Thread(new Runnable() {

                @Override
                public void run() {

                    File file = new File(localPath + "/"+"YourImageFile.jpg");
                    try {
                        file.createNewFile();
                        FileOutputStream ostream = new FileOutputStream(file);
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, ostream);
                        ostream.flush();
                        ostream.close();
                    } catch (IOException e) {
                        Log.e("IOException", e.getLocalizedMessage());
                    }
                }
            }).start();
        }

        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };
    return target;
}
person tariq101    schedule 21.10.2020

Как подсказывает Google, пока не забудьте добавить в манифест также возможность чтения на внешнем хранилище:

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Источник: http://developer.android.com/training/basics/data-storage/files.html#GetWritePermission

person Ludo    schedule 07.08.2015