Як завантажити та зберегти зображення в Android


Відповіді:


224

Редагувати станом на 30.12.2015 - Остаточний посібник із завантаження зображень


останнє основне оновлення: 31 березня 2016 р


TL; DR aka припиніть говорити, просто дайте мені код !!

Перейдіть до кінця цього допису, скопіюйте BasicImageDownloader(версія Java тут ) у свій проект, впровадьте OnImageLoaderListenerінтерфейс і все готово.

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


Оскільки цій публікації приділено досить багато уваги, я вирішив повністю її переробити, щоб люди не використовували застарілі технології, погані практики програмування або просто робили безглузді речі - наприклад, шукали "хаки" для запуску мережі в основному потоці або прийняти всі сертифікати SSL.

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

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

У своєму висновку наприкінці цього допису я поділюсь своєю скромною думкою щодо правильного використання кожного конкретного способу завантаження зображень, про який я згадав.

Почнемо з власної реалізації (код можна знайти в кінці допису). Перш за все, це Basic ImageDownloader і все. Все, що він робить, - це підключення до даної URL-адреси, зчитування даних та спроба їх декодування як Bitmap, викликаючи OnImageLoaderListenerзворотні виклики інтерфейсу, коли це доречно. Перевага такого підходу - він простий, і ви маєте чіткий огляд того, що відбувається. Хороший спосіб зробити це, якщо все, що вам потрібно, - це завантаження / відображення та збереження деяких зображень, тоді як ви не дбаєте про підтримку кеш-пам'яті / диска.

Примітка: у разі великих зображень вам може знадобитися зменшити їх .

-

Android DownloadManager - це спосіб дозволити системі обробляти завантаження за вас. Насправді він здатний завантажувати будь-які файли, не лише зображення. Ви можете дозволити завантаженню виконуватися безшумно і невидимо для користувача, або можете дозволити користувачеві бачити завантаження в області сповіщень. Ви також можете зареєструвати a, BroadcastReceiverщоб отримувати сповіщення після завершення завантаження. Налаштування досить просте, зверніться до зв’язаного проекту для зразка коду.

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

-

Тепер представлення чудового матеріалу - бібліотек. Вони можуть робити набагато більше, ніж просто завантажувати та відображати зображення, включаючи: створення та керування кеш-пам'яттю / диском, зміну розміру зображень, їх трансформацію тощо.

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

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

Можливо, ви захочете замінити свій ImageViewна Volley's NetworkImageView, тому завантаження в основному стає одношаровим:

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

Якщо вам потрібен більше контролю, ось як це виглядає, щоб створити за ImageRequestдопомогою Volley:

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

Варто згадати, що Volley має чудовий механізм обробки помилок, надаючи VolleyErrorклас, який допомагає визначити точну причину помилки. Якщо у вашому додатку багато мереж, а керування зображеннями не є його основною метою, то Volley це ідеальне рішення для вас.

-

Square's Picasso - це відома бібліотека, яка зробить все, що потрібно для завантаження зображень, за вас. Просто відобразити зображення за допомогою Пікассо так просто, як:

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

За замовчуванням Пікассо керує кешем диска / пам'яті, тому вам не потрібно турбуватися про це. Для більшого контролю ви можете реалізувати Targetінтерфейс і використовувати його для завантаження вашого зображення - це забезпечить зворотні виклики, подібні до прикладу Volley. Перевірте демонстраційний проект для прикладів.

Пікассо також дозволяє застосовувати перетворення до завантаженого зображення, і навколо існують навіть інші бібліотеки, які розширюють ці 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 р. Але оскільки є багато авторів, ми можемо сподіватися, що Universal Image Loader буде жити далі.

-

Facebook Fresco - це найновіша та (IMO) найдосконаліша бібліотека, яка виводить управління зображеннями на новий рівень: від відмови Bitmapsвід купи Java (до Lollipop) до підтримки анімованих форматів та прогресивного потокового передавання JPEG .

Щоб дізнатись більше про ідеї та прийоми створення фрески, зверніться до цієї публікації .

Основне використання досить просте. Зверніть увагу, що вам потрібно буде зателефонувати Fresco.initialize(Context);лише один раз, бажано в Applicationкласі. Повторна ініціалізація фрески може призвести до непередбачуваної поведінки та помилок OOM.

Фреска використовує 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 для відстеження стану проміжного завантаження) або Universal Image Навантажувач
  • Якщо у вашому додатку лише зображення, вам потрібні розширені функції, такі як відображення анімованих форматів, і ви готові читати документи, перейдіть за допомогою Fresco .

Якщо ви це пропустили, посилання Github для демонстраційного проекту.


І ось BasicImageDownloader.java

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

public class BasicImageDownloader {

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

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

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


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

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

            private ImageError error;

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

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

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

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

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

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


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

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

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

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

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

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

        private ImageError error;

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

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

        @Override
        protected void onPostExecute(Void result) {
            listener.onBitmapSaved();
        }
    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }

public static Bitmap readFromDisk(@NonNull File imageFile) {
    if (!imageFile.exists() || imageFile.isDirectory()) return null;
    return BitmapFactory.decodeFile(imageFile.getAbsolutePath());
}

public interface OnImageReadListener {
    void onImageRead(Bitmap bitmap);
    void onReadFailed();
}

public static void readFromDiskAsync(@NonNull File imageFile, @NonNull final OnImageReadListener listener) {
    new AsyncTask<String, Void, Bitmap>() {
        @Override
        protected Bitmap doInBackground(String... params) {
            return BitmapFactory.decodeFile(params[0]);
        }

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

    public static final class ImageError extends Throwable {

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


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

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

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

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

А як щодо зворотного виклику onPictureTaken (), який надає зображення у вигляді байта [], чи можна отримати URL-адресу цього зображення прямо з камери? Або базовий старий outputStream () - єдиний спосіб в Android зберегти знімок, зроблений камерою, без використання вбудованого наміру? Це здається дивним, тому що природне, що потрібно зробити після onPictureTaken, звичайно, це зберегти його. Чи немає для цього особливої ​​підтримки?
Томбола

2
@Tombola Привіт! Ця публікація стосується завантаження зображення з Інтернету. Але щоб відповісти на ваше запитання (наскільки я зрозумів): загальноприйнятим способом збереження зображення камери є отримання шляху з CursoronActivityResult()методі), а потім створення Bitmapвикористовуваного шляху. І так, ви не уникнете використання a FileOutputStreamта a, ByteArrayOutputStreamякщо хочете зберегти це зображення на SD.
Droidman

@BartBurg це питання стосується завантаження та збереження зображення. Але в якийсь момент ви маєте рацію, оскільки існує метод запису, для повноти також повинен існувати метод читання. Незабаром я додаю його в наступному оновленні до цієї публікації.
Droidman

Ви можете навести приклад, використовуючи цей BasicImageDownloader?
Jaydev

1
@JaydevKalivarapu перевірте демонстраційний додаток на GitHub ( вихідний клас, що містить приклад )
Droidman

35

Я щойно вирішив цю проблему, і я хотів би поділитися повним кодом, який можна завантажити, зберегти на SDCard (і приховати ім'я файлу) та отримати зображення, і нарешті він перевіряє, чи зображення вже є. 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() ; 
            }

Примітка : хоча цей приклад загалом може працювати, він не надає жодної обробки помилок, а також не має певного базового розуміння AsyncTaskпереваг (правильне використання параметризації, паралельне виконання ..). Для отримання детальної інформації зверніться до моїх прикладів нижче. PS . для тих, хто може подумати, що я написав це для просування власного коду з будь-якої причини: ні, я просто вказую на проблеми, які я бачу в наведеному прикладі.
Droidman

Так Droidman, я з вами згоден. Цей фрагмент коду слід сприймати як шаблон, і його потрібно заповнити самостійно, включаючи обробку помилок. До речі, у вашому коді також бракує обробки помилок. Що станеться з вашим підключенням та потоками у випадку IOException?
Androider

@Androider, будь ласка, уважніше розгляньте мій downloadметод, зокрема doInBackgroundметод завдання, який я використовую. IOExceptionБ земля в catch (Throwable e)блоці, в результаті чого ImageErrorповертаються і onError()зворотний виклик , які ініційований. ImageErrorОб'єкт буде містити вихідні трасування стеки і причину відбулисяException
Droidman

1
Так, але ваше з’єднання не буде від’єднано, а ваші потоки не закриті
Androider

@Androider ах, я бачу вашу думку. Хоча я ніколи не помічав підозрілих витоків на своїх тестових пристроях, ця поведінка може відрізнятися на інших пристроях. Дякую за підказку - я оновив код
Droidman

33

Навіщо вам справді потрібен власний код для його завантаження? Як щодо просто передачі вашого 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);

}

1
Ідеальний приклад для api> 8.
Jeeten Parmar

Ваш код ідеально підходить для одного зображення! що я можу зробити, якщо я хочу завантажити більше (100 зображень) з мого файлу зображень на своєму сервері ???
Kostantinos Ibra

Я отримую повідомлення про те, що файл пошкоджено!
Ануп

2
Можливо, .setDestinationInExternalPublicDir ("/ AnhsirkDasarp", "fileName.jpg");
Експерт хоче бути

2
Звідки ми знаємо, чи завантажено його успішно, чи не вдалося
Прабс

12

це може вам допомогти ..

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

6

У мене є просте рішення, яке працює ідеально. Код не мій, я знайшов його за цим посиланням . Ось такі кроки:

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 у класі вашого заняття, або в методі 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 ("файл", "my_image.jpeg видалено!");


0

цей код чудово працює в моєму проекті

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

1
привіт, дякую за внесок. Зверніть увагу, що наведений вище підхід до збереження зображення в приватному сховищі програми має 2 недоліки: 1) зображення буде доступне лише для вашої програми та 2) слід уникати збереження зображень у приватному сховищі програми, оскільки воно не призначене для великого вмісту.
Droidman

0

Спробуйте це

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

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

ВИХІДвведіть тут опис зображення

Переконайтеся, що ви додали дозвіл на запис даних у пам’ять

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

0

Пост @Droidman досить вичерпний. Волейбол добре працює з невеликими даними в кілька кбайт. Коли я спробував використати '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;
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.