How to Download and Save an Image in Android

How to download and save an image in Android

Edit as of 30.12.2015 - The Ultimate Guide to image downloading


last major update: Mar 31 2016


TL;DR a.k.a. stop talking, just give me the code!!

Skip to the bottom of this post, copy the BasicImageDownloader (javadoc version here)
into your project, implement the OnImageLoaderListener interface
and you're done.

Note: though the BasicImageDownloader handles possible errors
and will prevent your app from crashing in case anything goes wrong, it will not perform
any post-processing (e.g. downsizing) on the downloaded Bitmaps.


Since this post has received quite a lot of attention, I have decided to completely rework it to prevent the folks from using deprecated technologies, bad programming practices or just doing silly things - like looking for "hacks" to run network on the main thread or accept all SSL certs.

I've created a demo project named "Image Downloader" that demonstrates how to download (and save) an image using my own downloader implementation, the Android's built-in DownloadManager as well as some popular open-source libraries. You can view the complete source code or download the project on GitHub.

Note: I have not adjusted the permission management for SDK 23+ (Marshmallow) yet, thus the project is targeting SDK 22 (Lollipop).

In my conclusion at the end of this post I will share my humble opinion about the proper use-case for each particular way of image downloading I've mentioned.

Let's start with an own implementation (you can find the code at the end of the post). First of all, this is a BasicImageDownloader and that's it. All it does is connecting to the given url, reading the data and trying to decode it as a Bitmap, triggering the OnImageLoaderListener interface callbacks when appropriate.
The advantage of this approach - it is simple and you have a clear overview of what's going on. A good way to go if all you need is downloading/displaying and saving some images, whilst you don't care about maintaining a memory/disk cache.

Note: in case of large images, you might need to scale them
down.

--

Android DownloadManager is a way to let the system handle the download for you. It's actually capable of downloading any kind of files, not just images. You may let your download happen silently and invisible to the user, or you can enable the user to see the download in the notification area. You can also register a BroadcastReceiver to get notified after you download is complete. The setup is pretty much straightforward, refer to the linked project for sample code.

Using the DownloadManager is generally not a good idea if you also want to display the image, since you'd need to read and decode the saved file instead of just setting the downloaded Bitmap into an ImageView. The DownloadManager also does not provide any API for you app to track the download progress.

--

Now the introduction of the great stuff - the libraries. They can do much more than just downloading and displaying images, including: creating and managing the memory/disk cache, resizing images, transforming them and more.

I will start with Volley, a powerful library created by Google and covered by the official documentation. While being a general-purpose networking library not specializing on images, Volley features quite a powerful API for managing images.

You will need to implement a Singleton class for managing Volley requests and you are good to go.

You might want to replace your ImageView with Volley's NetworkImageView, so the download basically becomes a one-liner:

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

If you need more control, this is what it looks like to create an ImageRequest with 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
}
});

It is worth mentioning that Volley features an excellent error handling mechanism by providing the VolleyError class that helps you to determine the exact cause of an error. If your app does a lot of networking and managing images isn't its main purpose, then Volley it a perfect fit for you.

--

Square's Picasso is a well-known library which will do all of the image loading stuff for you. Just displaying an image using Picasso is as simple as:

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

By default, Picasso manages the disk/memory cache so you don't need to worry about that. For more control you can implement the Target interface and use it to load your image into - this will provide callbacks similar to the Volley example. Check the demo project for examples.

Picasso also lets you apply transformations to the downloaded image and there are even other libraries around that extend those API. Also works very well in a RecyclerView/ListView/GridView.

--

Universal Image Loader is an another very popular library serving the purpose of image management. It uses its own ImageLoader that (once initialized) has a global instance which can be used to download images in a single line of code:

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

If you want to track the download progress or access the downloaded Bitmap:

 ImageLoader.getInstance().displayImage(url, myImageView, opts, 
new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
//do stuff
}

@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
//do stuff
}

@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
//do stuff
}

@Override
public void onLoadingCancelled(String imageUri, View view) {
//do stuff
}
}, new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current, int total) {
//do stuff
}
});

The opts argument in this example is a DisplayImageOptions object. Refer to the demo project to learn more.

Similar to Volley, UIL provides the FailReason class that enables you to check what went wrong on download failure. By default, UIL maintains a memory/disk cache if you don't explicitly tell it not to do so.

Note: the author has mentioned that he is no longer maintaining the project as of Nov 27th, 2015. But since there are many contributors, we can hope that the Universal Image Loader will live on.

--

Facebook's Fresco is the newest and (IMO) the most advanced library that takes image management to a new level: from keeping Bitmaps off the java heap (prior to Lollipop) to supporting animated formats and progressive JPEG streaming.

To learn more about ideas and techniques behind Fresco, refer to this post.

The basic usage is quite simple. Note that you'll need to call Fresco.initialize(Context); only once, preferable in the Application class. Initializing Fresco more than once may lead to unpredictable behavior and OOM errors.

Fresco uses Drawees to display images, you can think of them as of 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" />

As you can see, a lot of stuff (including transformation options) gets already defined in XML, so all you need to do to display an image is a one-liner:

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

Fresco provides an extended customization API, which, under circumstances, can be quite complex and requires the user to read the docs carefully (yes, sometimes you need to RTFM).

I have included examples for progressive JPEG's and animated images into the sample project.


Conclusion - "I have learned about the great stuff, what should I use now?"

Note that the following text reflects my personal opinion and should
not be taken as a postulate.

  • If you only need to download/save/display some images, don't plan to use them in a Recycler-/Grid-/ListView and don't need a whole bunch of images to be display-ready, the BasicImageDownloader should fit your needs.
  • If your app saves images (or other files) as a result of a user or an automated action and you don't need the images to be displayed often, use the Android DownloadManager.
  • In case your app does a lot of networking, transmits/receives JSON data, works with images, but those are not the main purpose of the app, go with Volley.
  • Your app is image/media-focused, you'd like to apply some transformations to images and don't want to bother with complex API: use Picasso (Note: does not provide any API to track the intermediate download status) or Universal Image Loader
  • If your app is all about images, you need advanced features like displaying animated formats and you are ready to read the docs, go with Fresco.

In case you missed that, the Github link for the demo project.


And here's the 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;
}
}
}

Save Image From URL in a Specific Location in Android

image url to bitmap

 try {
URL url = new URL("http://....");
Bitmap image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch(IOException e) {
System.out.println(e);
}




public void saveImageToExternal(String imgName, Bitmap bm) throws IOException {
//Create Path to save Image
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES+appFolder); //Creates app specific folder
path.mkdirs();
File imageFile = new File(path, imgName+".png"); // Imagename.png
FileOutputStream out = new FileOutputStream(imageFile);
try{
bm.compress(Bitmap.CompressFormat.PNG, 100, out); // Compress Image
out.flush();
out.close();

// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(context,new String[] { imageFile.getAbsolutePath() }, null,new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
} catch(Exception e) {
throw new IOException();
}
}

download image and save in gallery?

I did it by using AsyncTask to download image from a URL.

  class DownloadImageAsyncTask extends AsyncTask<Void, Void, Void> 
{

@Override
protected Void doInBackground(Void... params)
{
// TODO Auto-generated method stub
downloadImages();
return null;
}

@Override
protected void onPostExecute(Void result)
{
// TODO Auto-generated method stub
super.onPostExecute(result);

}

}

This is the download image function which you can get reference from many sites.

private void downloadImages()
{

URL imageUrl; //your URL from which image to be downloaded
String domain;
try
{
imageUrl = new URL("your URL");

HttpURLConnection urlConnection;
try
{
urlConnection = (HttpURLConnection) imageUrl.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
String path = getExternalCacheDir() + File.separator + getResources().getString(R.string.app_name);
File f = new File(path);
if (!f.exists())
{
f.mkdirs();
}
File file = new File(f, "ImageName.png"); // Here you can save the image with name and extension
if (!file.exists())
{
file.createNewFile();
}
FileOutputStream fileOutput = new FileOutputStream(file);
InputStream inputStream = urlConnection.getInputStream();
byte[] buffer = new byte[1024];
int bufferLength = 0; // used to store a temporary size of the
// buffer
while ((bufferLength = inputStream.read(buffer)) > 0)
{
fileOutput.write(buffer, 0, bufferLength);
}
fileOutput.close();
}
catch (IOException e)
{
e.printStackTrace();
}

}
catch (MalformedURLException e)
{
e.printStackTrace();
}

}

Then in your Adapter class you can set the image by doing this:

holder.propic.setImageDrawable(Drawable.createFromPath(getExternalCacheDir() + File.separator
+ getResources().getString(R.string.app_name) + File.separator + "ImageName.png")); //Exact path where the image is saved.

Hope it might help you.

How to download and save image into the app directory using Ion library

Ion.with(this).load("url").withBitmap().asBitmap()
.setCallback(new FutureCallback<Bitmap>() {
@Override
public void onCompleted(Exception e, Bitmap result) {
// do something with your bitmap
}
});

You will get a bitmap on the onCompleted callback, that is your image as bitmap. Now you can save easily where you want.

For save bitmap :

//the string here is the file name
public static void saveBitmap(Context context, Bitmap original, String name) {
try {
File imageFile = createPngFileFromBitmap(name, original);
addImageToGallery(imageFile.getAbsolutePath(), context);


} catch (FileNotFoundException e) {
e.printStackTrace();
}

}

public static void addImageToGallery(final String filePath, final Context context) {

ContentValues values = new ContentValues();

values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.MediaColumns.DATA, filePath);


context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}

public static File createPngFileFromBitmap(String name, Bitmap bitmap) throws FileNotFoundException {
File picture = createPngFile(name);
OutputStream stream = new FileOutputStream(picture);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);

return picture;
}

private static File createPngFile(String name) {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Your folder name");
if (!file.mkdirs()) {
} else {
file.mkdirs();
}
return new File(file.getAbsolutePath(), name);
}

Good luck!

Download image, save to store, but image gallery cant see that image

You are saving your image to your internal app storage, you need to make it available to the gallery (external storage of your app) in order to be available outside your app.

Try this method:

private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
//change mCurrentPhotoPath for your imagepath
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}

Reference to the documentation:

https://developer.android.com/training/camera/photobasics.html#TaskGallery

https://developer.android.com/guide/topics/data/data-storage.html#filesExternal

Download Image and store it in Gallery app in android

As @RoyFalk pointed out you got 2 issues in your code.

So you can go with this code snippet

String filename = "filename.jpg";
String downloadUrlOfImage = "YOUR_LINK_THAT_POINTS_IMG_ON_WEBSITE";
File direct =
new File(Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
.getAbsolutePath() + "/" + DIR_NAME + "/");


if (!direct.exists()) {
direct.mkdir();
Log.d(LOG_TAG, "dir created for first time");
}

DownloadManager dm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
Uri downloadUri = Uri.parse(downloadUrlOfImage);
DownloadManager.Request request = new DownloadManager.Request(downloadUri);
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(false)
.setTitle(filename)
.setMimeType("image/jpeg")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES,
File.separator + DIR_NAME + File.separator + filename);

dm.enqueue(request);

And you will see Image inside the gallery application under your DIR_NAME.
Hope this will help you.

Save image to a download folder on Android

If you want it visible in your gallery, you need to save it to a more public location. Before you enqueue the request, add:

String fileName = "local.png";

request.setDestinationInExternalPublicDir( //
Environment.DIRECTORY_PICTURES, fileName );

This will also require you to add the following permission in your manifest.xml

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

Download images and save it

For you to support offline mode, You need to Save the image on your disk because when your cache is cleared, The image is cleared as well.

You can easily use Glide to Solve this, also storing on device and retrieving

You can Learn more about Glide here http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

/** Download the image using Glide **/

Bitmap theBitmap = null;
theBitmap = Glide.
with(YourActivity.this).
load("Url of your image").
asBitmap().
into(-1, -1).
get();

saveToInternalStorage(theBitmap, getApplicationContext(), "your preferred image name");

/** Save it on your device **/

public String saveToInternalStorage(Bitmap bitmapImage, Context context, String name){


ContextWrapper cw = new ContextWrapper(context);
// path to /data/data/yourapp/app_data/imageDir

String name_="foldername"; //Folder

Related Topics



Leave a reply



Submit