Cách tải xuống và lưu hình ảnh trong Android


Câu trả lời:


224

Chỉnh sửa kể từ ngày 30.12.2015 - Hướng dẫn cuối cùng để tải xuống hình ảnh


cập nhật chính cuối cùng: ngày 31 tháng 3 năm 2016


TL; DR aka thôi đừng nói nữa, chỉ mình code thôi !!

Chuyển đến cuối bài đăng này, sao chép BasicImageDownloader(phiên bản javadoc tại đây ) vào dự án của bạn, triển khai OnImageLoaderListenergiao diện và bạn đã hoàn tất.

Lưu ý : mặc dù BasicImageDownloaderxử lý các lỗi có thể xảy ra và sẽ ngăn ứng dụng của bạn gặp sự cố trong trường hợp xảy ra sự cố, nhưng nó sẽ không thực hiện bất kỳ quá trình xử lý hậu kỳ nào (ví dụ: giảm kích thước) trên bản tải xuống Bitmaps.


Vì bài đăng này đã nhận được khá nhiều sự chú ý, tôi đã quyết định làm lại hoàn toàn nó để ngăn mọi người sử dụng các công nghệ không được dùng nữa, các phương pháp lập trình tồi hoặc chỉ làm những điều ngớ ngẩn - như tìm kiếm "hack" để chạy mạng trên chuỗi chính hoặc chấp nhận tất cả chứng chỉ SSL.

Tôi đã tạo một dự án demo có tên "Image Downloader" trình bày cách tải xuống (và lưu) một hình ảnh bằng cách sử dụng triển khai trình tải xuống của riêng tôi, tích hợp sẵn của Android DownloadManagercũng như một số thư viện nguồn mở phổ biến. Bạn có thể xem mã nguồn hoàn chỉnh hoặc tải xuống dự án trên GitHub .

Lưu ý : Tôi chưa điều chỉnh việc quản lý quyền cho SDK 23+ (Marshmallow), do đó, dự án đang nhắm mục tiêu SDK 22 (Lollipop).

Trong phần kết luận của tôi ở cuối bài đăng này, tôi sẽ chia sẻ ý kiến ​​khiêm tốn của mình về trường hợp sử dụng thích hợp cho từng cách tải hình ảnh cụ thể mà tôi đã đề cập.

Hãy bắt đầu với một triển khai riêng (bạn có thể tìm thấy mã ở cuối bài đăng). Trước hết, đây là một ImageDownloader cơ bản và thế là xong. Tất cả những gì nó làm là kết nối với url đã cho, đọc dữ liệu và cố gắng giải mã nó dưới dạng Bitmap, kích hoạt OnImageLoaderListenerlệnh gọi lại giao diện khi thích hợp. Ưu điểm của cách tiếp cận này - nó đơn giản và bạn có cái nhìn tổng quan rõ ràng về những gì đang diễn ra. Một cách tốt để thực hiện nếu tất cả những gì bạn cần là tải xuống / hiển thị và lưu một số hình ảnh, trong khi bạn không quan tâm đến việc duy trì bộ nhớ đệm / đĩa đệm.

Lưu ý: trong trường hợp hình ảnh lớn, bạn có thể cần phải thu nhỏ chúng lại .

-

Android DownloadManager là một cách để hệ thống xử lý quá trình tải xuống cho bạn. Nó thực sự có khả năng tải xuống bất kỳ loại tệp nào, không chỉ hình ảnh. Bạn có thể để quá trình tải xuống diễn ra âm thầm và ẩn với người dùng hoặc bạn có thể cho phép người dùng xem tải xuống trong khu vực thông báo. Bạn cũng có thể đăng ký một BroadcastReceiverđể nhận thông báo sau khi tải xong. Việc thiết lập khá đơn giản, hãy tham khảo dự án được liên kết để biết mã mẫu.

Sử dụng DownloadManagerthông thường không phải là một ý tưởng hay nếu bạn cũng muốn hiển thị hình ảnh, vì bạn cần đọc và giải mã tệp đã lưu thay vì chỉ đặt tệp đã tải xuống Bitmapthành tệp ImageView. Nó DownloadManagercũng không cung cấp bất kỳ API nào cho ứng dụng của bạn để theo dõi tiến trình tải xuống.

-

Bây giờ là phần giới thiệu của những thứ tuyệt vời - các thư viện. Họ có thể làm được nhiều việc hơn là chỉ tải xuống và hiển thị hình ảnh, bao gồm: tạo và quản lý bộ nhớ đệm / bộ nhớ đệm đĩa, thay đổi kích thước hình ảnh, chuyển đổi chúng và hơn thế nữa.

Tôi sẽ bắt đầu với Volley , một thư viện mạnh mẽ do Google tạo ra và được bao phủ bởi tài liệu chính thức. Mặc dù là một thư viện mạng có mục đích chung không chuyên về hình ảnh, nhưng Volley có một API khá mạnh để quản lý hình ảnh.

Bạn sẽ cần phải triển khai một lớp Singleton để quản lý các yêu cầu Volley và bạn đã sẵn sàng.

Bạn có thể muốn thay thế của bạn ImageViewbằng của Volley NetworkImageView, vì vậy tải xuống về cơ bản trở thành một lớp lót:

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

Nếu bạn cần kiểm soát nhiều hơn, đây là những gì nó trông giống như để tạo một ImageRequestvới 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
                }
            });

Điều đáng nói là Volley có cơ chế xử lý lỗi tuyệt vời bằng cách cung cấp VolleyErrorlớp giúp bạn xác định chính xác nguyên nhân gây ra lỗi. Nếu ứng dụng của bạn thực hiện nhiều mạng và quản lý hình ảnh không phải là mục đích chính của nó, thì Volley nó hoàn toàn phù hợp với bạn.

-

Square's Picasso là một thư viện nổi tiếng sẽ thực hiện tất cả các công việc tải hình ảnh cho bạn. Chỉ hiển thị hình ảnh bằng Picasso đơn giản như:

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

Theo mặc định, Picasso quản lý đĩa đệm / bộ nhớ đệm nên bạn không cần phải lo lắng về điều đó. Để có nhiều quyền kiểm soát hơn, bạn có thể triển khai Targetgiao diện và sử dụng nó để tải hình ảnh của bạn vào - điều này sẽ cung cấp các lệnh gọi lại tương tự như ví dụ Volley. Kiểm tra dự án demo để biết các ví dụ.

Picasso cũng cho phép bạn áp dụng các phép biến đổi cho hình ảnh đã tải xuống và thậm chí còn có các thư viện khác mở rộng API đó. Cũng hoạt động rất tốt trong a RecyclerView/ ListView/ GridView.

-

Universal Image Loader là một thư viện rất phổ biến khác phục vụ mục đích quản lý hình ảnh. Nó sử dụng cái riêng ImageLoader(sau khi được khởi tạo) có một phiên bản chung có thể được sử dụng để tải xuống hình ảnh trong một dòng mã:

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

Nếu bạn muốn theo dõi tiến trình tải xuống hoặc truy cập vào phần đã tải xuống 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
                }
            });

Đối optssố trong ví dụ này là một DisplayImageOptionsđối tượng. Tham khảo dự án demo để tìm hiểu thêm.

Tương tự như Volley, UIL cung cấp FailReasonlớp cho phép bạn kiểm tra xem có gì sai khi tải xuống không. Theo mặc định, UIL duy trì bộ nhớ cache trong bộ nhớ / ổ đĩa nếu bạn không yêu cầu rõ ràng nó không làm như vậy.

Lưu ý : tác giả đã đề cập rằng anh ấy không còn duy trì dự án kể từ ngày 27 tháng 11 năm 2015. Nhưng vì có nhiều người đóng góp, chúng tôi có thể hy vọng rằng Universal Image Loader sẽ tồn tại.

-

Fresco của Facebook là thư viện mới nhất và (IMO) tiên tiến nhất đưa việc quản lý hình ảnh lên một tầm cao mới: từ việc loại Bitmapsbỏ đống java (trước Lollipop) đến việc hỗ trợ các định dạng độngphát trực tuyến JPEG liên tục .

Để tìm hiểu thêm về các ý tưởng và kỹ thuật đằng sau Fresco, hãy tham khảo bài đăng này .

Cách sử dụng cơ bản khá đơn giản. Lưu ý rằng bạn sẽ chỉ cần gọi Fresco.initialize(Context);một lần, tốt nhất là trong Applicationlớp. Việc khởi tạo Fresco nhiều lần có thể dẫn đến các hành vi không thể đoán trước và lỗi OOM.

Fresco sử dụng Drawees để hiển thị hình ảnh, bạn có thể coi chúng là 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" />

Như bạn có thể thấy, rất nhiều thứ (bao gồm các tùy chọn chuyển đổi) đã được định nghĩa trong XML, vì vậy tất cả những gì bạn cần làm để hiển thị hình ảnh là một lớp lót:

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

Fresco cung cấp một API tùy chỉnh mở rộng, trong các trường hợp, có thể khá phức tạp và yêu cầu người dùng đọc tài liệu cẩn thận (vâng, đôi khi bạn cần phải RTFM).

Tôi đã đưa các ví dụ cho các hình ảnh động và hình ảnh động JPEG tiến bộ vào dự án mẫu.


Kết luận - "Tôi đã học về những thứ tuyệt vời, tôi nên sử dụng những gì bây giờ?"

Lưu ý rằng văn bản sau đây phản ánh quan điểm cá nhân của tôi và không nên được coi là một định đề.

  • Nếu bạn chỉ cần tải xuống / lưu / hiển thị một số hình ảnh, không có kế hoạch sử dụng chúng trong một Recycler-/Grid-/ListViewvà không cần cả đống hình ảnh để sẵn sàng hiển thị, BasicImageDownloader sẽ phù hợp với nhu cầu của bạn.
  • Nếu ứng dụng của bạn lưu hình ảnh (hoặc các tệp khác) do người dùng hoặc một hành động tự động và bạn không cần hình ảnh được hiển thị thường xuyên, hãy sử dụng Android DownloadManager .
  • Trong trường hợp ứng dụng của bạn thực hiện nhiều kết nối mạng, truyền / nhận JSONdữ liệu, hoạt động với hình ảnh nhưng đó không phải là mục đích chính của ứng dụng, hãy sử dụng Volley .
  • Ứng dụng của bạn tập trung vào hình ảnh / phương tiện, bạn muốn áp dụng một số biến đổi cho hình ảnh và không muốn bận tâm đến API phức tạp: hãy sử dụng Picasso (Lưu ý: không cung cấp bất kỳ API nào để theo dõi trạng thái tải xuống trung gian) hoặc Universal Image Bộ tải
  • Nếu ứng dụng của bạn là về hình ảnh, bạn cần các tính năng nâng cao như hiển thị các định dạng hoạt hình và bạn đã sẵn sàng đọc tài liệu, hãy đến với Fresco .

Trong trường hợp bạn bỏ lỡ điều đó, hãy liên kết Github cho dự án demo.


Và đây là 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;
        }
      }
   }

Còn về lệnh gọi lại onPictureTaken () cho hình ảnh dưới dạng byte [], người ta có thể lấy URL đến hình ảnh đó, trực tiếp từ máy ảnh không? Hay là outputStream () cũ cơ bản là cách duy nhất trong Android để lưu ảnh được chụp bởi máy ảnh mà không sử dụng ý định tích hợp? Điều đó có vẻ lạ, bởi vì điều tự nhiên phải làm sau onPictureTaken tất nhiên là lưu nó. Không có hỗ trợ cụ thể nào để làm điều đó?
Tombola

2
@Tombola Chào bạn! Bài đăng này là về việc tải xuống một bức tranh từ web. Nhưng để trả lời câu hỏi của bạn (theo như tôi đã hiểu): cách phổ biến để lưu ảnh máy ảnh là lấy đường dẫn của nó từ Cursor(trong onActivityResult()phương thức), sau đó tạo Bitmapđường dẫn bằng đường dẫn đó. Và có, bạn sẽ không thể sử dụng a FileOutputStreamvà a ByteArrayOutputStreamnếu muốn lưu hình ảnh này vào SD.
Droidman

@BartBurg câu hỏi này là về tải xuống và lưu hình ảnh. Nhưng bạn đã đúng ở một số điểm, vì có một phương thức ghi, cũng nên có một phương pháp đọc để hoàn thiện. Tôi sẽ bổ sung nó trong bản cập nhật tiếp theo cho bài đăng này sớm.
Droidman

Bạn có thể vui lòng cung cấp một ví dụ sử dụng BasicImageDownloader này không?
Jaydev

1
@JaydevKalivarapu vui lòng kiểm tra ứng dụng demo trên GitHub ( lớp nguồn chứa ví dụ )
Droidman

35

Tôi vừa mới giải quyết vấn đề này và tôi muốn chia sẻ mã hoàn chỉnh có thể tải xuống, lưu vào sdcard (và ẩn tên tệp) và truy xuất hình ảnh và cuối cùng nó kiểm tra xem hình ảnh đã ở đó chưa. Url đến từ cơ sở dữ liệu nên tên tệp có thể là duy nhất dễ dàng bằng cách sử dụng id.

tải xuống hình ảnh đầu tiên

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

Sau đó, tạo một lớp để lưu và truy xuất các tệp

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

Sau đó, để truy cập hình ảnh trước tiên hãy kiểm tra xem nó đã ở đó chưa nếu chưa thì tải xuống

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

Lưu ý : mặc dù ví dụ này nói chung có thể hoạt động, nhưng nó không cung cấp bất kỳ xử lý lỗi nào và cũng thiếu một số hiểu biết cơ bản về các AsyncTaskưu điểm của '(sử dụng đúng tham số, thực hiện song song ..). Vui lòng tham khảo các ví dụ của tôi dưới đây để biết thêm chi tiết. Tái bút . cho những người có thể nghĩ rằng tôi viết bài này để quảng cáo mã của riêng tôi vì bất kỳ lý do gì: không, tôi chỉ đang chỉ ra những vấn đề tôi có thể thấy trong ví dụ đã cho.
Droidman

Vâng Droidman, tôi đồng ý với bạn. Đoạn mã này nên được coi như một miếng đệm lót và người ta phải tự hoàn thành nó, bao gồm cả việc xử lý lỗi. Nhân tiện, mã của bạn cũng thiếu xử lý lỗi. Điều gì sẽ xảy ra với kết nối và luồng của bạn trong trường hợp IOException?
Androider

@Androider vui lòng xem kỹ downloadphương pháp của tôi , đặc biệt là doInBackgroundphương pháp của tác vụ mà tôi sử dụng. An IOExceptionsẽ hạ cánh trong catch (Throwable e)khối, dẫn đến việc ImageErrorđược trả về và onError()kích hoạt lệnh gọi lại. Các ImageErrorđối tượng sẽ chứa stack trace gốc và nguyên nhân của việc xảy raException
Droidman

1
Có, nhưng kết nối của bạn sẽ không bị ngắt kết nối và luồng của bạn sẽ không bị đóng
Androider

@Androider à, tôi hiểu ý của bạn. Mặc dù tôi chưa bao giờ nhận thấy bất kỳ rò rỉ đáng ngờ nào trên các thiết bị thử nghiệm của mình, nhưng hành vi này có thể khác trên các thiết bị khác. Cảm ơn vì gợi ý - Tôi đã cập nhật mã
Droidman

32

Tại sao bạn thực sự cần mã của riêng mình để tải xuống? Bạn chỉ cần chuyển URI của bạn tới Trình quản lý tải xuống?

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
Ví dụ hoàn hảo cho api> 8.
Jeeten Parmar

Mã của bạn hoạt động hoàn hảo cho một hình ảnh! Tôi có thể làm gì nếu tôi muốn tải xuống nhiều hơn (100 ảnh) từ tệp hình ảnh trong máy chủ của mình ???
Kostantinos Ibra

Tôi nhận được thông báo rằng tệp bị hỏng!
Anup

2
Có thể, .setDestinationInExternalPublicDir ("/ AnhsirkDasarp", "fileName.jpg");
Chuyên gia muốn trở thành

2
Làm thế nào để chúng tôi biết liệu nó được tải xuống thành công hay thất bại
Prabs

12

nó có thể giúp bạn ..

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

Tôi có một giải pháp đơn giản đang hoạt động hoàn hảo. Mã không phải của tôi, tôi tìm thấy nó trên liên kết này . Dưới đây là các bước để làm theo:

1. Trước khi tải hình ảnh về, chúng ta hãy viết phương thức lưu bitmap thành tệp hình ảnh trong bộ nhớ trong của android. Nó cần một ngữ cảnh, tốt hơn là sử dụng pass trong ngữ cảnh ứng dụng bởi getApplicationContext (). Phương thức này có thể được đưa vào lớp Hoạt động của bạn hoặc các lớp sử dụng khác.

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. Bây giờ chúng ta có một phương pháp để lưu bitmap thành tệp hình ảnh trong andorid, chúng ta hãy viết AsyncTask để tải hình ảnh theo url. Lớp riêng này cần được đặt trong lớp Hoạt động của bạn như một lớp con. Sau khi hình ảnh được tải xuống, trong phương thức onPostExecute, nó gọi phương thức saveImage được xác định ở trên để lưu hình ảnh. Lưu ý, tên hình ảnh được mã hóa cứng là “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 để tải xuống hình ảnh đã được xác định, nhưng chúng ta cần thực thi nó để chạy AsyncTask đó. Để làm như vậy, hãy viết dòng này trong phương thức onCreate trong lớp Hoạt động của bạn hoặc trong phương thức onClick của một nút hoặc những nơi khác mà bạn thấy phù hợp.

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

Hình ảnh sẽ được lưu trong /data/data/your.app.packagename/files/my_image.jpeg, hãy kiểm tra bài đăng này để truy cập thư mục này từ thiết bị của bạn.

IMO này giải quyết vấn đề! Nếu bạn muốn các bước tiếp theo như tải hình ảnh, bạn có thể làm theo các bước bổ sung sau:

4. Sau khi hình ảnh được tải xuống, chúng ta cần một cách để tải ảnh bitmap từ bộ nhớ trong, để chúng ta có thể sử dụng nó. Hãy viết phương thức tải ảnh bitmap. Phương thức này lấy hai tham số, một ngữ cảnh và một tên tệp hình ảnh, không có đường dẫn đầy đủ, context.openFileInput (imageName) sẽ tra cứu tệp tại thư mục lưu khi tên tệp này được lưu trong phương thức saveImage ở trên.

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. Bây giờ chúng tôi có mọi thứ chúng tôi cần để thiết lập hình ảnh của ImageView hoặc bất kỳ Chế độ xem nào khác mà bạn muốn sử dụng hình ảnh. Khi chúng tôi lưu hình ảnh, chúng tôi đã mã hóa tên hình ảnh là “my_image.jpeg”, bây giờ chúng tôi có thể chuyển tên hình ảnh này vào phương thức loadImageBitmap ở trên để lấy bitmap và đặt nó thành ImageView.

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

6. Để có được hình ảnh đầy đủ đường dẫn theo tên hình ảnh.

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

7. Kiểm tra xem tệp hình ảnh có tồn tại không.

Tệp tin =

getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
  1. Để xóa tệp hình ảnh.

    File file = getApplicationContext (). GetFileStreamPath ("my_image.jpeg"); if (file.delete ()) Log.d ("tệp", "my_image.jpeg bị xóa!");


0

mã này chạy hoàn hảo trong dự án của tôi

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
xin chào, cảm ơn vì sự đóng góp. Lưu ý rằng phương pháp lưu hình ảnh trên bộ nhớ riêng của ứng dụng có 2 nhược điểm: 1) hình ảnh sẽ chỉ có sẵn cho ứng dụng của bạn và 2) bạn nên tránh lưu hình ảnh vào bộ nhớ riêng của ứng dụng vì nó không dành cho nội dung lớn.
Droidman

0

Thử cái này

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

ĐẦU RAnhập mô tả hình ảnh ở đây

Đảm bảo bạn đã thêm quyền ghi dữ liệu vào bộ nhớ

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.