Làm cách nào để bạn tải xuống và lưu hình ảnh từ một url nhất định trong Android?
Câu trả lời:
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 khaiOnImageLoaderListener
giao diện và bạn đã hoàn tất.Lưu ý : mặc dù
BasicImageDownloader
xử 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ốngBitmaps
.
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 DownloadManager
cũ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 OnImageLoaderListener
lệ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 DownloadManager
thô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 Bitmap
thành tệp ImageView
. Nó DownloadManager
cũ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 ImageView
bằ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 ImageRequest
vớ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 VolleyError
lớ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 Target
giao 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 opts
số 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 FailReason
lớ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 Bitmaps
bỏ đống java (trước Lollipop) đến việc hỗ trợ các định dạng động và phá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 Application
lớ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 Drawee
s để hiển thị hình ảnh, bạn có thể coi chúng là ImageView
s:
<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.
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 đề.
Recycler-/Grid-/ListView
và 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.JSON
dữ 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 .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;
}
}
}
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 FileOutputStream
và a ByteArrayOutputStream
nếu muốn lưu hình ảnh này vào SD.
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() ;
}
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.
download
phương pháp của tôi , đặc biệt là doInBackground
phương pháp của tác vụ mà tôi sử dụng. An IOException
sẽ 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
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);
}
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();
}
}
});
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!");
Để 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!");
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();
}
}
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();
}
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();
}
}
}
}
Đả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"/>
Như Google đã nói, hiện tại, đừng quên thêm cũng có thể đọc được trên bộ nhớ ngoài vào tệp kê khai:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Nguồn: http://developer.android.com/training/basics/data-storage/files.html#GetWritePermission