คุณดาวน์โหลดและบันทึกรูปภาพจาก URL ที่กำหนดใน Android ได้อย่างไร
คุณดาวน์โหลดและบันทึกรูปภาพจาก URL ที่กำหนดใน Android ได้อย่างไร
คำตอบ:
การอัปเดตที่สำคัญล่าสุด: 31 มีนาคม 2559
TL; DR aka หยุดพูดแค่ให้รหัส !!
ข้ามไปที่ด้านล่างของโพสต์นี้คัดลอก
BasicImageDownloader
(รุ่น javadoc ที่นี่ ) ลงในโปรเจ็กต์ของคุณใช้OnImageLoaderListener
อินเทอร์เฟซเท่านี้ก็เสร็จเรียบร้อยหมายเหตุ : แม้ว่า
BasicImageDownloader
ข้อผิดพลาดที่อาจเกิดขึ้นได้และจะป้องกันไม่ให้แอปของคุณหยุดทำงานในกรณีที่มีข้อผิดพลาดเกิดขึ้น แต่จะไม่ดำเนินการหลังการประมวลผลใด ๆ (เช่นการลดขนาด) ในBitmaps
แอป
เนื่องจากโพสต์นี้ได้รับความสนใจค่อนข้างมากฉันจึงตัดสินใจทำใหม่ทั้งหมดเพื่อป้องกันไม่ให้ผู้คนใช้เทคโนโลยีที่เลิกใช้งานการเขียนโปรแกรมที่ไม่ดีหรือเพียงแค่ทำเรื่องโง่ ๆ เช่นกำลังมองหา "แฮ็ก" เพื่อเรียกใช้เครือข่ายในเธรดหลักหรือ ยอมรับใบรับรอง SSL ทั้งหมด
ฉันได้สร้างโปรเจ็กต์สาธิตชื่อ "Image Downloader" ซึ่งสาธิตวิธีดาวน์โหลด (และบันทึก) รูปภาพโดยใช้การติดตั้งโปรแกรมดาวน์โหลดของฉันเอง, แอนดรอยด์ในตัวและDownloadManager
ไลบรารีโอเพนซอร์สยอดนิยมบางรายการ คุณสามารถดูรหัสต้นฉบับที่สมบูรณ์หรือดาวน์โหลดโครงการบน GitHub
หมายเหตุ : ฉันยังไม่ได้ปรับการจัดการสิทธิ์สำหรับ SDK 23+ (Marshmallow) ดังนั้นโครงการจึงกำหนดเป้าหมายไปที่ SDK 22 (Lollipop)
ในบทสรุปของฉันในตอนท้ายของโพสต์นี้ฉันจะแบ่งปันความคิดเห็นอันต่ำต้อยของฉันเกี่ยวกับกรณีการใช้งานที่เหมาะสมสำหรับวิธีการดาวน์โหลดภาพแต่ละวิธีที่ฉันได้กล่าวถึง
เริ่มต้นด้วยการใช้งานของตัวเอง (คุณสามารถดูรหัสได้ที่ท้ายโพสต์) ก่อนอื่นนี่คือBasic ImageDownloader และนั่นแหล่ะ เพียงแค่เชื่อมต่อกับ url ที่กำหนดอ่านข้อมูลและพยายามถอดรหัสเป็น a Bitmap
เรียกใช้OnImageLoaderListener
อินเทอร์เฟซเรียกกลับตามความเหมาะสม ข้อดีของแนวทางนี้ - ง่ายและคุณมีภาพรวมที่ชัดเจนว่าเกิดอะไรขึ้น วิธีที่ดีหากสิ่งที่คุณต้องการคือการดาวน์โหลด / แสดงและบันทึกภาพบางภาพในขณะที่คุณไม่สนใจเกี่ยวกับการดูแลหน่วยความจำ / ดิสก์แคช
หมายเหตุ: ในกรณีของภาพขนาดใหญ่คุณอาจจำเป็นต้องปรับขนาดพวกเขาลง
-
Android DownloadManagerเป็นวิธีที่จะให้ระบบจัดการการดาวน์โหลดให้คุณ มันสามารถดาวน์โหลดไฟล์ได้ทุกประเภทไม่ใช่แค่รูปภาพเท่านั้น คุณอาจปล่อยให้การดาวน์โหลดของคุณเกิดขึ้นโดยเงียบและผู้ใช้มองไม่เห็นหรือคุณสามารถเปิดให้ผู้ใช้เห็นการดาวน์โหลดในพื้นที่แจ้งเตือน คุณยังสามารถลงทะเบียนBroadcastReceiver
เพื่อรับการแจ้งเตือนหลังจากดาวน์โหลดเสร็จสิ้น การตั้งค่าค่อนข้างตรงไปตรงมาดูโครงการที่เชื่อมโยงสำหรับโค้ดตัวอย่าง
โดยDownloadManager
ทั่วไปการใช้สิ่งนี้ไม่ใช่ความคิดที่ดีหากคุณต้องการแสดงภาพด้วยเนื่องจากคุณจำเป็นต้องอ่านและถอดรหัสไฟล์ที่บันทึกไว้แทนที่จะตั้งค่าไฟล์ที่ดาวน์โหลดBitmap
มาเป็นImageView
ไฟล์. DownloadManager
ยังไม่ได้ให้ API ใด ๆ สำหรับคุณ app เพื่อติดตามความคืบหน้าการดาวน์โหลด
-
ตอนนี้การแนะนำสิ่งที่ยอดเยี่ยมนั่นคือห้องสมุด พวกเขาสามารถทำอะไรได้มากกว่าการดาวน์โหลดและแสดงภาพรวมถึงการสร้างและจัดการแคชของหน่วยความจำ / ดิสก์การปรับขนาดภาพการเปลี่ยนแปลงและอื่น ๆ
ฉันจะเริ่มต้นด้วยVolleyห้องสมุดอันทรงพลังที่ Google สร้างขึ้นและครอบคลุมโดยเอกสารอย่างเป็นทางการ ในขณะที่เป็นไลบรารีเครือข่ายที่ใช้งานทั่วไปซึ่งไม่ได้เชี่ยวชาญด้านรูปภาพ แต่ Volley มี API ที่ทรงพลังสำหรับจัดการรูปภาพ
คุณจะต้องใช้คลาสSingletonเพื่อจัดการคำขอวอลเลย์และคุณก็พร้อมที่จะไป
คุณอาจต้องการแทนที่ของคุณImageView
ด้วย Volley's NetworkImageView
ดังนั้นการดาวน์โหลดโดยทั่วไปจะกลายเป็นซับเดียว:
((NetworkImageView) findViewById(R.id.myNIV)).setImageUrl(url, MySingleton.getInstance(this).getImageLoader());
หากคุณต้องการการควบคุมที่มากขึ้นนี่คือสิ่งที่ดูเหมือนว่าจะสร้างImageRequest
ด้วยวอลเลย์:
ImageRequest imgRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
//do stuff
}
}, 0, 0, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888,
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//do stuff
}
});
เป็นมูลค่าการกล่าวขวัญว่า Volley มีกลไกการจัดการข้อผิดพลาดที่ยอดเยี่ยมโดยการจัดเตรียมVolleyError
คลาสที่ช่วยให้คุณระบุสาเหตุที่แท้จริงของข้อผิดพลาด หากแอปของคุณสร้างเครือข่ายจำนวนมากและการจัดการรูปภาพไม่ใช่จุดประสงค์หลักแสดงว่า Volley เหมาะสำหรับคุณ
-
Picassoของ Square เป็นห้องสมุดที่มีชื่อเสียงซึ่งจะทำหน้าที่โหลดรูปภาพทั้งหมดให้คุณ เพียงแค่แสดงภาพโดยใช้ Picasso ก็ทำได้ง่ายๆดังนี้:
Picasso.with(myContext)
.load(url)
.into(myImageView);
ตามค่าเริ่มต้น Picasso จะจัดการแคชของดิสก์ / หน่วยความจำดังนั้นคุณจึงไม่ต้องกังวลเรื่องนี้ สำหรับการควบคุมที่มากขึ้นคุณสามารถใช้Target
อินเทอร์เฟซและใช้อินเทอร์เฟซเพื่อโหลดรูปภาพของคุณ - สิ่งนี้จะให้การเรียกกลับที่คล้ายกับตัวอย่างของวอลเลย์ ตรวจสอบโครงการสาธิตเพื่อดูตัวอย่าง
Picasso ยังให้คุณใช้การแปลงกับรูปภาพที่ดาวน์โหลดมาและยังมีไลบรารีอื่นๆ ที่ขยาย API เหล่านั้นอีกด้วย นอกจากนี้ยังทำงานได้เป็นอย่างดีในRecyclerView
/ /ListView
GridView
-
Universal Image Loaderเป็นอีกหนึ่งไลบรารีที่ได้รับความนิยมอย่างมากเพื่อจุดประสงค์ในการจัดการรูปภาพ มันใช้ของตัวเองImageLoader
ที่ (เมื่อเริ่มต้นแล้ว) มีอินสแตนซ์ส่วนกลางซึ่งสามารถใช้เพื่อดาวน์โหลดภาพในโค้ดบรรทัดเดียว:
ImageLoader.getInstance().displayImage(url, myImageView);
หากคุณต้องการติดตามความคืบหน้าในการดาวน์โหลดหรือเข้าถึงสิ่งที่ดาวน์โหลดมาBitmap
:
ImageLoader.getInstance().displayImage(url, myImageView, opts,
new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
//do stuff
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
//do stuff
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
//do stuff
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
//do stuff
}
}, new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current, int total) {
//do stuff
}
});
opts
โต้แย้งในตัวอย่างนี้เป็นDisplayImageOptions
วัตถุ อ้างถึงโครงการสาธิตเพื่อเรียนรู้เพิ่มเติม
เช่นเดียวกับ Volley UIL มีFailReason
คลาสที่ให้คุณตรวจสอบสิ่งที่ผิดพลาดจากความล้มเหลวในการดาวน์โหลด ตามค่าเริ่มต้น UIL จะเก็บรักษาหน่วยความจำ / ดิสก์แคชหากคุณไม่ได้บอกอย่างชัดเจนว่าไม่ทำเช่นนั้น
หมายเหตุ : ผู้เขียนได้กล่าวว่าเขาไม่ได้ดูแลโครงการอีกต่อไป ณ วันที่ 27 พฤศจิกายน 2015 แต่เนื่องจากมีผู้ร่วมให้ข้อมูลจำนวนมากเราหวังว่า Universal Image Loader จะใช้งานได้
-
ของ Facebook Frescoเป็นรุ่นใหม่ล่าสุดและ (IMO) ห้องสมุดที่ทันสมัยที่สุดที่จะจัดการภาพไปยังระดับใหม่: จากการรักษาBitmaps
ออกกองจาวา (ก่อนที่จะอมยิ้ม) จะให้การสนับสนุนรูปแบบภาพเคลื่อนไหวและความก้าวหน้า JPEG สตรีมมิ่ง
หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับแนวคิดและเทคนิคเบื้องหลัง Fresco โปรดดูที่โพสต์นี้
การใช้งานพื้นฐานค่อนข้างง่าย โปรดทราบว่าคุณจะต้องโทร Fresco.initialize(Context);
เพียงครั้งเดียวดีกว่าในApplication
ชั้นเรียน การเริ่มต้น Fresco มากกว่าหนึ่งครั้งอาจทำให้เกิดพฤติกรรมที่คาดเดาไม่ได้และข้อผิดพลาด OOM
Fresco ใช้Drawee
s ในการแสดงภาพคุณสามารถคิด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" />
อย่างที่คุณเห็นมีหลายสิ่ง (รวมถึงตัวเลือกการเปลี่ยนแปลง) ได้รับการกำหนดไว้แล้วใน XML ดังนั้นสิ่งที่คุณต้องทำเพื่อแสดงภาพคือซับเดียว:
mDrawee.setImageURI(Uri.parse(url));
Fresco มี API การปรับแต่งเพิ่มเติมซึ่งในบางสถานการณ์อาจค่อนข้างซับซ้อนและต้องการให้ผู้ใช้อ่านเอกสารอย่างรอบคอบ (ใช่บางครั้งคุณต้องใช้ RTFM)
ฉันได้รวมตัวอย่างสำหรับภาพ JPEG และภาพเคลื่อนไหวแบบโปรเกรสซีฟไว้ในโครงการตัวอย่าง
โปรดทราบว่าข้อความต่อไปนี้สะท้อนถึงความคิดเห็นส่วนตัวของฉันและไม่ควรนำมาเป็นสมมติฐาน
Recycler-/Grid-/ListView
และไม่จำเป็นต้องมีภาพจำนวนมากเพื่อพร้อมแสดงผลBasicImageDownloaderควรตรงกับความต้องการของคุณJSON
ข้อมูลการทำงานร่วมกับภาพ แต่ผู้ที่ไม่ได้เป็นวัตถุประสงค์หลักของแอปไปกับวอลเล่ย์ในกรณีที่คุณพลาดลิงก์ Githubสำหรับโครงการสาธิต
และนี่คือไฟล์ BasicImageDownloader.java
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Set;
public class BasicImageDownloader {
private OnImageLoaderListener mImageLoaderListener;
private Set<String> mUrlsInProgress = new HashSet<>();
private final String TAG = this.getClass().getSimpleName();
public BasicImageDownloader(@NonNull OnImageLoaderListener listener) {
this.mImageLoaderListener = listener;
}
public interface OnImageLoaderListener {
void onError(ImageError error);
void onProgressChange(int percent);
void onComplete(Bitmap result);
}
public void download(@NonNull final String imageUrl, final boolean displayProgress) {
if (mUrlsInProgress.contains(imageUrl)) {
Log.w(TAG, "a download for this url is already running, " +
"no further download will be started");
return;
}
new AsyncTask<Void, Integer, Bitmap>() {
private ImageError error;
@Override
protected void onPreExecute() {
mUrlsInProgress.add(imageUrl);
Log.d(TAG, "starting download");
}
@Override
protected void onCancelled() {
mUrlsInProgress.remove(imageUrl);
mImageLoaderListener.onError(error);
}
@Override
protected void onProgressUpdate(Integer... values) {
mImageLoaderListener.onProgressChange(values[0]);
}
@Override
protected Bitmap doInBackground(Void... params) {
Bitmap bitmap = null;
HttpURLConnection connection = null;
InputStream is = null;
ByteArrayOutputStream out = null;
try {
connection = (HttpURLConnection) new URL(imageUrl).openConnection();
if (displayProgress) {
connection.connect();
final int length = connection.getContentLength();
if (length <= 0) {
error = new ImageError("Invalid content length. The URL is probably not pointing to a file")
.setErrorCode(ImageError.ERROR_INVALID_FILE);
this.cancel(true);
}
is = new BufferedInputStream(connection.getInputStream(), 8192);
out = new ByteArrayOutputStream();
byte bytes[] = new byte[8192];
int count;
long read = 0;
while ((count = is.read(bytes)) != -1) {
read += count;
out.write(bytes, 0, count);
publishProgress((int) ((read * 100) / length));
}
bitmap = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.size());
} else {
is = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
}
} catch (Throwable e) {
if (!this.isCancelled()) {
error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
this.cancel(true);
}
} finally {
try {
if (connection != null)
connection.disconnect();
if (out != null) {
out.flush();
out.close();
}
if (is != null)
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
if (result == null) {
Log.e(TAG, "factory returned a null result");
mImageLoaderListener.onError(new ImageError("downloaded file could not be decoded as bitmap")
.setErrorCode(ImageError.ERROR_DECODE_FAILED));
} else {
Log.d(TAG, "download complete, " + result.getByteCount() +
" bytes transferred");
mImageLoaderListener.onComplete(result);
}
mUrlsInProgress.remove(imageUrl);
System.gc();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public interface OnBitmapSaveListener {
void onBitmapSaved();
void onBitmapSaveError(ImageError error);
}
public static void writeToDisk(@NonNull final File imageFile, @NonNull final Bitmap image,
@NonNull final OnBitmapSaveListener listener,
@NonNull final Bitmap.CompressFormat format, boolean shouldOverwrite) {
if (imageFile.isDirectory()) {
listener.onBitmapSaveError(new ImageError("the specified path points to a directory, " +
"should be a file").setErrorCode(ImageError.ERROR_IS_DIRECTORY));
return;
}
if (imageFile.exists()) {
if (!shouldOverwrite) {
listener.onBitmapSaveError(new ImageError("file already exists, " +
"write operation cancelled").setErrorCode(ImageError.ERROR_FILE_EXISTS));
return;
} else if (!imageFile.delete()) {
listener.onBitmapSaveError(new ImageError("could not delete existing file, " +
"most likely the write permission was denied")
.setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
return;
}
}
File parent = imageFile.getParentFile();
if (!parent.exists() && !parent.mkdirs()) {
listener.onBitmapSaveError(new ImageError("could not create parent directory")
.setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
return;
}
try {
if (!imageFile.createNewFile()) {
listener.onBitmapSaveError(new ImageError("could not create file")
.setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
return;
}
} catch (IOException e) {
listener.onBitmapSaveError(new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION));
return;
}
new AsyncTask<Void, Void, Void>() {
private ImageError error;
@Override
protected Void doInBackground(Void... params) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(imageFile);
image.compress(format, 100, fos);
} catch (IOException e) {
error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
this.cancel(true);
} finally {
if (fos != null) {
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
protected void onCancelled() {
listener.onBitmapSaveError(error);
}
@Override
protected void onPostExecute(Void result) {
listener.onBitmapSaved();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static Bitmap readFromDisk(@NonNull File imageFile) {
if (!imageFile.exists() || imageFile.isDirectory()) return null;
return BitmapFactory.decodeFile(imageFile.getAbsolutePath());
}
public interface OnImageReadListener {
void onImageRead(Bitmap bitmap);
void onReadFailed();
}
public static void readFromDiskAsync(@NonNull File imageFile, @NonNull final OnImageReadListener listener) {
new AsyncTask<String, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
return BitmapFactory.decodeFile(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null)
listener.onImageRead(bitmap);
else
listener.onReadFailed();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageFile.getAbsolutePath());
}
public static final class ImageError extends Throwable {
private int errorCode;
public static final int ERROR_GENERAL_EXCEPTION = -1;
public static final int ERROR_INVALID_FILE = 0;
public static final int ERROR_DECODE_FAILED = 1;
public static final int ERROR_FILE_EXISTS = 2;
public static final int ERROR_PERMISSION_DENIED = 3;
public static final int ERROR_IS_DIRECTORY = 4;
public ImageError(@NonNull String message) {
super(message);
}
public ImageError(@NonNull Throwable error) {
super(error.getMessage(), error.getCause());
this.setStackTrace(error.getStackTrace());
}
public ImageError setErrorCode(int code) {
this.errorCode = code;
return this;
}
public int getErrorCode() {
return errorCode;
}
}
}
Cursor
(ในonActivityResult()
วิธีการ) จากนั้นสร้างBitmap
โดยใช้เส้นทางนั้น และใช่คุณจะไม่หนีจากการใช้ a FileOutputStream
and a ByteArrayOutputStream
ถ้าคุณต้องการบันทึกภาพนี้ลงใน SD
ฉันเพิ่งมาจากการแก้ปัญหานี้และฉันต้องการแบ่งปันรหัสทั้งหมดที่สามารถดาวน์โหลดบันทึกลงใน sdcard (และซ่อนชื่อไฟล์) และดึงภาพและในที่สุดก็ตรวจสอบว่ามีภาพอยู่หรือไม่ url มาจากฐานข้อมูลดังนั้นชื่อไฟล์จึงไม่ซ้ำกันได้อย่างง่ายดายโดยใช้ id
ดาวน์โหลดภาพก่อน
private class GetImages extends AsyncTask<Object, Object, Object> {
private String requestUrl, imagename_;
private ImageView view;
private Bitmap bitmap ;
private FileOutputStream fos;
private GetImages(String requestUrl, ImageView view, String _imagename_) {
this.requestUrl = requestUrl;
this.view = view;
this.imagename_ = _imagename_ ;
}
@Override
protected Object doInBackground(Object... objects) {
try {
URL url = new URL(requestUrl);
URLConnection conn = url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
} catch (Exception ex) {
}
return null;
}
@Override
protected void onPostExecute(Object o) {
if(!ImageStorage.checkifImageExists(imagename_))
{
view.setImageBitmap(bitmap);
ImageStorage.saveToSdCard(bitmap, imagename_);
}
}
}
จากนั้นสร้างคลาสสำหรับบันทึกและเรียกไฟล์
public class ImageStorage {
public static String saveToSdCard(Bitmap bitmap, String filename) {
String stored = null;
File sdcard = Environment.getExternalStorageDirectory() ;
File folder = new File(sdcard.getAbsoluteFile(), ".your_specific_directory");//the dot makes this directory hidden to the user
folder.mkdir();
File file = new File(folder.getAbsoluteFile(), filename + ".jpg") ;
if (file.exists())
return stored ;
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
stored = "success";
} catch (Exception e) {
e.printStackTrace();
}
return stored;
}
public static File getImage(String imagename) {
File mediaImage = null;
try {
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root);
if (!myDir.exists())
return null;
mediaImage = new File(myDir.getPath() + "/.your_specific_directory/"+imagename);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return mediaImage;
}
public static boolean checkifImageExists(String imagename)
{
Bitmap b = null ;
File file = ImageStorage.getImage("/"+imagename+".jpg");
String path = file.getAbsolutePath();
if (path != null)
b = BitmapFactory.decodeFile(path);
if(b == null || b.equals(""))
{
return false ;
}
return true ;
}
}
จากนั้นในการเข้าถึงภาพก่อนอื่นให้ตรวจสอบว่ามีอยู่แล้วหากไม่มีให้ดาวน์โหลด
if(ImageStorage.checkifImageExists(imagename))
{
File file = ImageStorage.getImage("/"+imagename+".jpg");
String path = file.getAbsolutePath();
if (path != null){
b = BitmapFactory.decodeFile(path);
imageView.setImageBitmap(b);
}
} else {
new GetImages(imgurl, imageView, imagename).execute() ;
}
AsyncTask
ข้อดีของ (การใช้พาราเมตริเซชั่นอย่างเหมาะสมการดำเนินการแบบขนาน ... ) โปรดดูตัวอย่างของฉันด้านล่างสำหรับรายละเอียด ปล . สำหรับผู้ที่อาจคิดว่าฉันเขียนสิ่งนี้เพื่อโปรโมตโค้ดของตัวเองด้วยเหตุผลใดก็ตาม: ไม่ฉันแค่ชี้ประเด็นที่ฉันเห็นในตัวอย่างที่กำหนด
download
วิธีการของฉันโดยเฉพาะอย่างยิ่งdoInBackground
วิธีการของงานที่ฉันใช้ IOException
จะที่ดินในcatch (Throwable e)
บล็อกส่งผลให้ImageError
ถูกส่งกลับและonError()
โทรกลับถูกเรียก ImageError
วัตถุจะมีกองติดตามเดิมและสาเหตุของเกิดขึ้นException
ทำไมคุณต้องใช้รหัสของคุณเองเพื่อดาวน์โหลด แค่ส่ง URI ไปยัง Download manager ล่ะ
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);
}
มันอาจช่วยคุณได้ ..
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();
}
}
});
ฉันมีวิธีง่ายๆที่ทำงานได้อย่างสมบูรณ์ รหัสไม่ใช่ของฉันฉันพบมันในลิงค์นี้ ขั้นตอนดังต่อไปนี้:
1.ก่อนดาวน์โหลดภาพเรามาเขียนวิธีการบันทึกบิตแมปลงในไฟล์ภาพในที่เก็บข้อมูลภายในของ Android จำเป็นต้องมีบริบทดีกว่าที่จะใช้ pass ในบริบทของแอปพลิเคชันโดย getApplicationContext () วิธีนี้สามารถทิ้งลงในคลาสกิจกรรมของคุณหรือคลาสยูทิลอื่น ๆ
public void saveImage(Context context, Bitmap b, String imageName)
{
FileOutputStream foStream;
try
{
foStream = context.openFileOutput(imageName, Context.MODE_PRIVATE);
b.compress(Bitmap.CompressFormat.PNG, 100, foStream);
foStream.close();
}
catch (Exception e)
{
Log.d("saveImage", "Exception 2, Something went wrong!");
e.printStackTrace();
}
}
2.ตอนนี้เรามีวิธีบันทึกบิตแมปลงในไฟล์รูปภาพใน andorid มาเขียน AsyncTask เพื่อดาวน์โหลดรูปภาพตาม url คลาสส่วนตัวนี้จะต้องจัดให้อยู่ในคลาสกิจกรรมของคุณเป็นคลาสย่อย หลังจากดาวน์โหลดรูปภาพแล้วในเมธอด onPostExecute จะเรียกใช้เมธอด saveImage ที่กำหนดไว้ด้านบนเพื่อบันทึกรูปภาพ หมายเหตุชื่อรูปภาพจะถูกเข้ารหัสเป็น“ my_image.png”
private class DownloadImage extends AsyncTask<String, Void, Bitmap> {
private String TAG = "DownloadImage";
private Bitmap downloadImageBitmap(String sUrl) {
Bitmap bitmap = null;
try {
InputStream inputStream = new URL(sUrl).openStream(); // Download Image from URL
bitmap = BitmapFactory.decodeStream(inputStream); // Decode Bitmap
inputStream.close();
} catch (Exception e) {
Log.d(TAG, "Exception 1, Something went wrong!");
e.printStackTrace();
}
return bitmap;
}
@Override
protected Bitmap doInBackground(String... params) {
return downloadImageBitmap(params[0]);
}
protected void onPostExecute(Bitmap result) {
saveImage(getApplicationContext(), result, "my_image.png");
}
}
3. AsyncTask สำหรับดาวน์โหลดภาพถูกกำหนดไว้ แต่เราจำเป็นต้องดำเนินการเพื่อเรียกใช้ AsyncTask นั้น ในการทำเช่นนั้นให้เขียนบรรทัดนี้ในเมธอด onCreate ในคลาสกิจกรรมของคุณหรือในวิธีการ onClick ของปุ่มหรือที่อื่น ๆ ที่คุณเห็นว่าเหมาะสม
new DownloadImage().execute("http://developer.android.com/images/activity_lifecycle.png");
ควรบันทึกรูปภาพใน /data/data/your.app.packagename/files/my_image.jpeg ตรวจสอบโพสต์นี้เพื่อเข้าถึงไดเรกทอรีนี้จากอุปกรณ์ของคุณ
IMO ช่วยแก้ปัญหาได้! หากคุณต้องการขั้นตอนเพิ่มเติมเช่นโหลดรูปภาพคุณสามารถทำตามขั้นตอนเพิ่มเติมเหล่านี้:
4.หลังจากดาวน์โหลดรูปภาพแล้วเราต้องการวิธีโหลดบิตแมปภาพจากที่จัดเก็บข้อมูลภายในเพื่อให้เราสามารถใช้งานได้ มาเขียนวิธีการโหลดบิตแมปภาพ วิธีนี้ใช้พารามิเตอร์สองตัวบริบทและชื่อไฟล์รูปภาพโดยไม่มีพา ธ แบบเต็ม context.openFileInput (imageName) จะค้นหาไฟล์ที่ไดเร็กทอรีบันทึกเมื่อชื่อไฟล์นี้ถูกบันทึกในเมธอด saveImage ด้านบน
public Bitmap loadImageBitmap(Context context, String imageName) {
Bitmap bitmap = null;
FileInputStream fiStream;
try {
fiStream = context.openFileInput(imageName);
bitmap = BitmapFactory.decodeStream(fiStream);
fiStream.close();
} catch (Exception e) {
Log.d("saveImage", "Exception 3, Something went wrong!");
e.printStackTrace();
}
return bitmap;
}
5.ตอนนี้เรามีทุกสิ่งที่จำเป็นสำหรับการตั้งค่ารูปภาพของ ImageView หรือมุมมองอื่น ๆ ที่คุณต้องการใช้รูปภาพ เมื่อเราบันทึกรูปภาพเราฮาร์ดโค้ดชื่อรูปภาพเป็น“ my_image.jpeg” ตอนนี้เราสามารถส่งชื่อรูปภาพนี้ไปยังเมธอด loadImageBitmap ด้านบนเพื่อรับบิตแมปและตั้งค่าเป็น ImageView
someImageView.setImageBitmap(loadImageBitmap(getApplicationContext(), "my_image.jpeg"));
6. เพื่อให้ได้ภาพเต็มเส้นทางตามชื่อภาพ
File file = getApplicationContext().getFileStreamPath("my_image.jpeg");
String imageFullPath = file.getAbsolutePath();
7.ตรวจสอบว่ามีไฟล์ภาพอยู่หรือไม่
ไฟล์ไฟล์ =
getApplicationContext().getFileStreamPath("my_image.jpeg");
if (file.exists()) Log.d("file", "my_image.jpeg exists!");
เพื่อลบไฟล์ภาพ
ไฟล์ไฟล์ = getApplicationContext (). getFileStreamPath ("my_image.jpeg"); ถ้า (file.delete ()) Log.d ("file", "my_image.jpeg ถูกลบ!");
รหัสนี้ทำงานได้อย่างสมบูรณ์แบบในโครงการของฉัน
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();
}
}
}
}
ตรวจสอบว่าคุณได้เพิ่มสิทธิ์ในการเขียนข้อมูลในหน่วยความจำ
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
โพสต์ @Droidman ค่อนข้างครอบคลุม วอลเลย์ใช้งานได้ดีกับข้อมูลขนาดเล็กเพียงไม่กี่กิโลไบต์ เมื่อฉันพยายามใช้ 'BasicImageDownloader.java' Android Studio ได้เตือนฉันว่าคลาส AsyncTask ควรเป็นแบบคงที่หรืออาจมีการรั่วไหล ฉันใช้ Volley ในแอปทดสอบอื่นและยังคงขัดข้องเนื่องจากมีการรั่วไหลดังนั้นฉันจึงกังวลเกี่ยวกับการใช้ Volley สำหรับโปรแกรมดาวน์โหลดรูปภาพ (รูปภาพอาจมีขนาดไม่กี่ 100 kB)
ฉันใช้ Picasso และใช้งานได้ดีมีการเปลี่ยนแปลงเล็กน้อย (อาจเป็นการอัปเดตของ Picasso) จากสิ่งที่โพสต์ไว้ด้านบน รหัสด้านล่างใช้ได้กับฉัน:
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;
}
ตามที่ Google บอกในตอนนี้อย่าลืมเพิ่มที่อ่านได้ในที่จัดเก็บข้อมูลภายนอกในรายการ:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
ที่มา: http://developer.android.com/training/basics/data-storage/files.html#GetWritePermission