Cara mengunduh dan menyimpan gambar di Android


Jawaban:


224

Edit mulai 30.12.2015 - Panduan Utama untuk mengunduh gambar


pembaruan besar terakhir: 31 Maret 2016


TL; DR alias berhenti bicara, berikan saja kodenya !!

Lewati ke bagian bawah posting ini, salin BasicImageDownloader(versi javadoc di sini ) ke dalam proyek Anda, terapkan OnImageLoaderListenerantarmuka dan selesai.

Catatan : meskipun BasicImageDownloadermenangani kemungkinan kesalahan dan akan mencegah aplikasi Anda mogok jika terjadi kesalahan, ia tidak akan melakukan pasca-pemrosesan apa pun (misalnya, perampingan) pada unduhan Bitmaps.


Karena posting ini telah menerima cukup banyak perhatian, saya telah memutuskan untuk mengerjakannya ulang sepenuhnya untuk mencegah orang-orang menggunakan teknologi yang sudah usang, praktik pemrograman yang buruk atau hanya melakukan hal-hal konyol - seperti mencari "peretasan" untuk menjalankan jaringan di utas utama atau terima semua sertifikat SSL.

Saya telah membuat proyek demo bernama "Image Downloader" yang mendemonstrasikan cara mengunduh (dan menyimpan) gambar menggunakan implementasi pengunduh saya sendiri, bawaan Android DownloadManagerserta beberapa pustaka sumber terbuka populer. Anda dapat melihat kode sumber lengkap atau mengunduh proyek di GitHub .

Catatan : Saya belum menyesuaikan manajemen izin untuk SDK 23+ (Marshmallow), oleh karena itu proyek ini menargetkan SDK 22 (Lollipop).

Dalam kesimpulan saya di akhir posting ini, saya akan membagikan pendapat saya tentang kasus penggunaan yang tepat untuk setiap cara pengunduhan gambar tertentu yang telah saya sebutkan.

Mari kita mulai dengan implementasi sendiri (Anda dapat menemukan kode di akhir posting). Pertama-tama, ini adalah Basic ImageDownloader dan hanya itu. Yang dilakukannya hanyalah menghubungkan ke url yang diberikan, membaca data dan mencoba mendekodekannya sebagai Bitmap, memicu OnImageLoaderListenercallback antarmuka bila perlu. Keuntungan dari pendekatan ini - sederhana dan Anda memiliki gambaran yang jelas tentang apa yang terjadi. Cara yang baik untuk pergi jika yang Anda butuhkan hanyalah mengunduh / menampilkan dan menyimpan beberapa gambar, sementara Anda tidak peduli tentang mempertahankan cache memori / disk.

Catatan: jika gambarnya besar, Anda mungkin perlu memperkecilnya .

-

Android DownloadManager adalah cara agar sistem menangani unduhan untuk Anda. Ini sebenarnya mampu mengunduh semua jenis file, bukan hanya gambar. Anda dapat membiarkan unduhan Anda terjadi secara diam-diam dan tidak terlihat oleh pengguna, atau Anda dapat mengizinkan pengguna untuk melihat unduhan di area pemberitahuan. Anda juga dapat mendaftar BroadcastReceiveruntuk mendapatkan pemberitahuan setelah pengunduhan selesai. Penyiapannya cukup mudah, lihat proyek terkait untuk kode contoh.

Menggunakan file DownloadManagerumumnya bukan ide yang baik jika Anda juga ingin menampilkan gambar, karena Anda perlu membaca dan mendekode file yang disimpan alih-alih hanya mengatur unduhan Bitmapke file ImageView. The DownloadManagerjuga tidak menyediakan API bagi Anda app untuk melacak kemajuan-download.

-

Sekarang pengenalan hal-hal hebat - perpustakaan. Mereka dapat melakukan lebih dari sekadar mengunduh dan menampilkan gambar, termasuk: membuat dan mengelola cache memori / disk, mengubah ukuran gambar, mengubahnya, dan banyak lagi.

Saya akan mulai dengan Volley , perpustakaan hebat yang dibuat oleh Google dan tercakup dalam dokumentasi resmi. Meskipun menjadi pustaka jaringan tujuan umum yang tidak mengkhususkan diri pada gambar, Volley memiliki API yang cukup kuat untuk mengelola gambar.

Anda perlu menerapkan kelas Singleton untuk mengelola permintaan Volley dan Anda siap melakukannya.

Anda mungkin ingin mengganti ImageViewdengan Volley's NetworkImageView, sehingga unduhan pada dasarnya menjadi satu baris:

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

Jika Anda membutuhkan lebih banyak kontrol, seperti inilah cara membuat ImageRequestdengan 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
                }
            });

Perlu disebutkan bahwa Volley memiliki mekanisme penanganan kesalahan yang sangat baik dengan menyediakan VolleyErrorkelas yang membantu Anda menentukan penyebab pasti dari kesalahan. Jika aplikasi Anda melakukan banyak jaringan dan mengelola gambar bukanlah tujuan utamanya, maka Volley sangat cocok untuk Anda.

-

Square's Picasso adalah perpustakaan terkenal yang akan melakukan semua pemuatan gambar untuk Anda. Hanya menampilkan gambar menggunakan Picasso sesederhana:

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

Secara default, Picasso mengelola cache disk / memori sehingga Anda tidak perlu mengkhawatirkannya. Untuk kontrol lebih lanjut, Anda dapat mengimplementasikan Targetantarmuka dan menggunakannya untuk memuat gambar Anda - ini akan memberikan callback yang mirip dengan contoh Volley. Lihat proyek demo sebagai contoh.

Picasso juga memungkinkan Anda menerapkan transformasi ke gambar yang diunduh dan bahkan ada perpustakaan lain di sekitarnya yang memperluas API tersebut. Juga bekerja sangat baik di a RecyclerView/ ListView/ GridView.

-

Universal Image Loader adalah pustaka lain yang sangat populer yang melayani tujuan manajemen gambar. Ia menggunakan miliknya sendiri ImageLoaderyang (setelah diinisialisasi) memiliki contoh global yang dapat digunakan untuk mengunduh gambar dalam satu baris kode:

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

Jika Anda ingin melacak kemajuan unduhan atau mengakses unduhan 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
                }
            });

The optsargumen dalam contoh ini adalah DisplayImageOptionsobjek. Lihat proyek demo untuk mempelajari lebih lanjut.

Mirip dengan Volley, UIL menyediakan FailReasonkelas yang memungkinkan Anda memeriksa apa yang salah pada kegagalan pengunduhan. Secara default, UIL memelihara cache memori / disk jika Anda tidak secara eksplisit menyuruhnya untuk tidak melakukannya.

Catatan : penulis telah menyebutkan bahwa dia tidak lagi memelihara proyek tersebut mulai 27 November 2015. Tetapi karena ada banyak kontributor, kami berharap Universal Image Loader akan terus berjalan.

-

Fresco Facebook adalah perpustakaan terbaru dan (IMO) yang paling canggih yang membawa manajemen gambar ke tingkat yang baru: dari menjauhkan Bitmapstumpukan java (sebelum Lollipop) hingga mendukung format animasi dan streaming JPEG progresif .

Untuk mempelajari lebih lanjut tentang ide dan teknik di balik Fresco, lihat posting ini .

Penggunaan dasarnya cukup sederhana. Perhatikan bahwa Anda hanya perlu menelepon Fresco.initialize(Context);sekali, lebih disukai di Applicationkelas. Menginisialisasi Fresco lebih dari sekali dapat menyebabkan perilaku tak terduga dan kesalahan OOM.

Fresco menggunakan Drawees untuk menampilkan gambar, Anda dapat menganggapnya sebagai berikut ImageView:

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

Seperti yang Anda lihat, banyak hal (termasuk opsi transformasi) sudah ditentukan dalam XML, jadi yang perlu Anda lakukan untuk menampilkan gambar adalah satu baris:

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

Fresco menyediakan API penyesuaian tambahan, yang, dalam keadaan, bisa sangat kompleks dan mengharuskan pengguna untuk membaca dokumen dengan hati-hati (ya, terkadang Anda perlu RTFM).

Saya telah memasukkan contoh untuk JPEG progresif dan gambar animasi ke dalam proyek sampel.


Kesimpulan - "Saya telah belajar tentang hal-hal hebat, apa yang harus saya gunakan sekarang?"

Perhatikan bahwa teks berikut mencerminkan pendapat pribadi saya dan tidak boleh dianggap sebagai dalil.

  • Jika Anda hanya perlu mengunduh / menyimpan / menampilkan beberapa gambar, jangan berencana untuk menggunakannya dalam Recycler-/Grid-/ListViewdan tidak memerlukan banyak gambar untuk siap ditampilkan, BasicImageDownloader harus sesuai dengan kebutuhan Anda.
  • Jika aplikasi Anda menyimpan gambar (atau file lain) sebagai hasil dari pengguna atau tindakan otomatis dan Anda tidak memerlukan gambar untuk sering ditampilkan, gunakan Android DownloadManager .
  • Jika aplikasi Anda melakukan banyak jaringan, mengirim / menerima JSONdata, bekerja dengan gambar, tetapi itu bukan tujuan utama aplikasi, gunakan Volley .
  • Aplikasi Anda berfokus pada gambar / media, Anda ingin menerapkan beberapa transformasi ke gambar dan tidak ingin repot dengan API yang rumit: gunakan Picasso (Catatan: tidak menyediakan API apa pun untuk melacak status unduhan perantara) atau Gambar Universal Pemuat
  • Jika aplikasi Anda adalah tentang gambar, Anda memerlukan fitur-fitur canggih seperti menampilkan format animasi dan Anda siap untuk membaca dokumen, gunakan Fresco .

Jika Anda melewatkannya, tautan Github untuk proyek demo.


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

Bagaimana dengan callback onPictureTaken () yang memberikan gambar sebagai byte [], dapatkah seseorang mendapatkan URL untuk gambar itu, langsung dari kamera? Atau apakah outputStream () lama dasar satu-satunya cara di Android untuk menyimpan gambar yang diambil dengan kamera tanpa menggunakan maksud bawaan? Tampaknya aneh, karena hal wajar yang dilakukan setelah onPictureTaken tentu saja adalah menyimpannya. Apakah tidak ada dukungan khusus untuk melakukan itu?
Tombola

2
@Tombola Hai! Posting ini tentang mendownload gambar dari web. Tetapi untuk menjawab pertanyaan Anda (sejauh yang saya mengerti): cara umum untuk menyimpan gambar kamera adalah mendapatkan jalurnya dari Cursor(dalam onActivityResult()metode), kemudian membuat Bitmapmenggunakan jalur itu. Dan ya, Anda tidak akan lepas dari penggunaan a FileOutputStreamdan a ByteArrayOutputStreamjika Anda ingin menyimpan gambar ini ke SD.
Droidman

@BartBurg pertanyaan ini tentang mengunduh dan menyimpan gambar. Tapi Anda benar di beberapa titik, karena ada metode tulis, harus ada juga metode baca demi kelengkapan. Saya akan menambahkannya di pembaruan berikutnya ke posting ini segera.
Droidman

Bisakah Anda memberikan contoh menggunakan BasicImageDownloader ini?
Jaydev

1
@JaydevKalivarapu silakan periksa aplikasi demo di GitHub ( kelas sumber yang berisi contoh )
Droidman

35

Saya baru saja menyelesaikan masalah ini dan saya ingin membagikan kode lengkap yang dapat mengunduh, menyimpan ke sdcard (dan menyembunyikan nama file) dan mengambil gambar dan akhirnya memeriksa apakah gambar tersebut sudah ada. Url berasal dari database sehingga nama file bisa dengan mudah menggunakan id.

gambar unduhan pertama

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

Kemudian buat kelas untuk menyimpan dan mengambil file

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

Kemudian Untuk mengakses gambar pertama periksa apakah sudah ada jika belum kemudian unduh

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

Catatan : walaupun contoh ini secara umum dapat bekerja, ia tidak memberikan penanganan error dan juga tidak memiliki beberapa pemahaman dasar tentang AsyncTaskkeuntungannya (penggunaan yang tepat dari parametrization, eksekusi paralel ..). Silakan lihat contoh saya di bawah ini untuk detailnya. PS . bagi mereka yang mungkin berpikir saya menulis ini untuk mempromosikan kode saya sendiri karena alasan apa pun: tidak, saya hanya menunjukkan masalah yang dapat saya lihat dalam contoh yang diberikan.
Droidman

Ya Droidman, saya setuju dengan Anda. Potongan kode ini harus dianggap sebagai tamplate dan harus diselesaikan sendiri, termasuk penanganan kesalahan. Ngomong-ngomong, kode Anda juga tidak memiliki penanganan kesalahan. Apa yang akan terjadi pada koneksi dan streaming Anda jika terjadi IOException?
Androider

@Androider harap lihat lebih dekat downloadmetode saya , terutama doInBackgroundmetode tugas yang saya gunakan. Sebuah IOExceptionakan mendarat di catch (Throwable e)blok tersebut, menghasilkan ImageErroryang dikembalikan dan onError()panggilan balik dipicu. The ImageErrorobjek akan berisi jejak stack asli dan penyebab terjadiException
Droidman

1
Ya, tetapi koneksi Anda tidak akan terputus dan streaming Anda tidak akan ditutup
Androider

@Androider ah, saya mengerti maksud Anda. Meskipun saya tidak pernah melihat adanya kebocoran yang mencurigakan di perangkat pengujian saya, perilaku ini mungkin berbeda di perangkat lain. Terima kasih atas petunjuknya - Saya telah memperbarui kode
Droidman

33

Mengapa Anda benar-benar membutuhkan kode Anda sendiri untuk mendownloadnya? Bagaimana kalau meneruskan URI Anda ke Pengelola unduhan?

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
Contoh sempurna untuk api> 8.
Jeeten Parmar

Kode Anda berfungsi sempurna untuk satu gambar! apa yang dapat saya lakukan jika saya ingin mengunduh lebih banyak (100 gambar) dari file gambar saya di server saya ???
Kostantinos Ibra

Saya mendapat pesan yang mengatakan file rusak!
Anup

2
Mungkin, .setDestinationInExternalPublicDir ("/ AnhsirkDasarp", "fileName.jpg");
Pakar ingin menjadi

2
Bagaimana kita tahu apakah itu diunduh dengan sukses atau gagal
Prabs

12

itu mungkin membantu Anda ..

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

Saya memiliki solusi sederhana yang bekerja dengan sempurna. Kode ini bukan milik saya, saya menemukannya di tautan ini . Berikut langkah-langkah yang harus diikuti:

1. Sebelum mendownload image, mari kita tulis metode untuk menyimpan bitmap menjadi file image di penyimpanan internal di android. Ini membutuhkan konteks, lebih baik menggunakan pass dalam konteks aplikasi dengan getApplicationContext (). Metode ini bisa dibuang ke kelas Activity Anda atau kelas util lainnya.

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. Sekarang kita memiliki metode untuk menyimpan bitmap menjadi file gambar di andorid, mari kita tulis AsyncTask untuk mengunduh gambar dengan url. Kelas privat ini perlu ditempatkan di kelas Aktivitas Anda sebagai subkelas. Setelah gambar diunduh, dalam metode onPostExecute, ini memanggil metode saveImage yang ditentukan di atas untuk menyimpan gambar. Perhatikan, nama gambar di-hardcode sebagai “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 untuk mengunduh gambar telah ditentukan, tetapi kita perlu menjalankannya untuk menjalankan AsyncTask tersebut. Untuk melakukannya, tulis baris ini dalam metode onCreate di kelas Activity Anda, atau dalam metode onClick dari sebuah tombol atau tempat lain yang Anda inginkan.

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

Gambar harus disimpan di /data/data/your.app.packagename/files/my_image.jpeg, periksa posting ini untuk mengakses direktori ini dari perangkat Anda.

IMO ini menyelesaikan masalah! Jika Anda menginginkan langkah lebih lanjut seperti memuat gambar, Anda dapat mengikuti langkah-langkah tambahan ini:

4. Setelah gambar diunduh, kita membutuhkan cara untuk memuat bitmap gambar dari penyimpanan internal, sehingga kita dapat menggunakannya. Mari tulis metode untuk memuat bitmap gambar. Metode ini mengambil dua parameter, konteks dan nama file gambar, tanpa path lengkap, context.openFileInput (imageName) akan mencari file di direktori save ketika nama file ini disimpan dalam metode saveImage di atas.

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. Sekarang kami memiliki semua yang kami butuhkan untuk menyetel gambar ImageView atau Tampilan lain yang Anda suka untuk menggunakan gambar tersebut. Saat kami menyimpan gambar, kami melakukan hardcode nama gambar sebagai "my_image.jpeg", sekarang kami dapat meneruskan nama gambar ini ke metode loadImageBitmap di atas untuk mendapatkan bitmap dan menyetelnya ke ImageView.

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

6. Untuk mendapatkan gambar path lengkap dengan nama gambar.

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

7. Periksa apakah file gambar ada.

File file =

getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
  1. Untuk menghapus file gambar.

    File file = getApplicationContext (). GetFileStreamPath ("my_image.jpeg"); if (file.delete ()) Log.d ("file", "my_image.jpeg dihapus!");


0

kode ini berjalan dengan sempurna dalam proyek saya

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
hai, terima kasih atas kontribusinya. Perhatikan bahwa pendekatan di atas untuk menyimpan gambar di penyimpanan pribadi aplikasi memiliki 2 kelemahan: 1) gambar hanya akan tersedia untuk aplikasi Anda dan 2) Anda harus menghindari penyimpanan gambar ke penyimpanan pribadi aplikasi karena tidak ditujukan untuk konten besar.
Droidman

0

Coba ini

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

KELUARANmasukkan deskripsi gambar di sini

Pastikan Anda menambahkan izin untuk menulis data di memori

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

0

Posting @Droid cukup komprehensif. Volley berfungsi baik dengan data kecil beberapa kbytes. Ketika saya mencoba menggunakan 'BasicImageDownloader.java', Android Studio memberi saya peringatan bahwa kelas AsyncTask harus statis atau mungkin ada kebocoran. Saya menggunakan Volley di aplikasi pengujian lain dan terus mogok karena kebocoran, jadi saya khawatir menggunakan Volley untuk pengunduh gambar (gambar dapat berukuran 100 kB).

Saya menggunakan Picasso dan berfungsi dengan baik, ada sedikit perubahan (mungkin pembaruan di Picasso) dari apa yang diposting di atas. Kode di bawah ini berhasil untuk saya:

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.