Android Display Images from Firebase Storage

Firebase is a backend service which provides various services like database, cloud functions, storage.Cloud Storage for Firebase allows secure cloud storage of files, user generated content like images, audio. Firebase allows easy access of stored files and it can be used to store app content like images and files.

Let’s check how to use Firebase Storage apis to fetch and display image in an ImageView.

Before we jump to android project lets upload some files in the cloud storage. Open firebase dashboard and create storage bucket here.

Open storage rules tab and paste these values

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read;
      allow write: if request.auth != null;
    }
  }
}

This will make read operations public while only authenticated users would be able to write into the storage bucket.

Upload an image in storage bucket and copy the path as shown in the image below. For this example we will use this file path to display in our android project.

Create new android project. Or in existing project open Tools -> Firebase and click on Storage to link firebase project with Android project. If you are unfamiliar with this operation then go through this post to check how to connect firebase in android studio.

In build.gradle add dependencies.

    implementation 'com.google.firebase:firebase-storage:16.0.5'
    implementation 'com.google.firebase:firebase-auth:16.1.0'

    implementation 'com.github.bumptech.glide:glide:4.8.0'
    implementation "com.zlc.glide:webpdecoder:1.3.4.8.0"
    annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

We have added firebase storage, authentication and Glide which is image loading and caching library.

Create Glide library module to add support for firebase storage.

import android.content.Context;

import com.bumptech.glide.Glide;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
import com.google.firebase.storage.StorageReference;

import java.io.InputStream;

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {
        registry.append(StorageReference.class, InputStream.class,
                new FirebaseImageLoader.Factory());
    }
}

FirebaseImageLoader class is a utility class which can be used to load Firebase Storage reference url directly by Glide App.

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.StreamDownloadTask;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.MessageDigest;

public class FirebaseImageLoader implements ModelLoader<StorageReference, InputStream> {

    private static final String TAG = "FirebaseImageLoader";


    /**
     * Factory to create {@link FirebaseImageLoader}.
     */
    public static class Factory implements ModelLoaderFactory<StorageReference, InputStream> {

        @NonNull
        @Override
        public ModelLoader<StorageReference, InputStream> build(@NonNull MultiModelLoaderFactory factory) {
            return new FirebaseImageLoader();
        }

        @Override
        public void teardown() {
            // No-op
        }
    }

    @Nullable
    @Override
    public LoadData<InputStream> buildLoadData(@NonNull StorageReference reference,
                                               int height,
                                               int width,
                                               @NonNull Options options) {
        return new LoadData<>(
                new FirebaseStorageKey(reference),
                new FirebaseStorageFetcher(reference));
    }

    @Override
    public boolean handles(@NonNull StorageReference reference) {
        return true;
    }

    private static class FirebaseStorageKey implements Key {

        private StorageReference mRef;

        public FirebaseStorageKey(StorageReference ref) {
            mRef = ref;
        }

        @Override
        public void updateDiskCacheKey(@NonNull MessageDigest digest) {
            digest.update(mRef.getPath().getBytes(Charset.defaultCharset()));
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            FirebaseStorageKey key = (FirebaseStorageKey) o;

            return mRef.equals(key.mRef);
        }

        @Override
        public int hashCode() {
            return mRef.hashCode();
        }
    }

    private static class FirebaseStorageFetcher implements DataFetcher<InputStream> {

        private StorageReference mRef;
        private StreamDownloadTask mStreamTask;
        private InputStream mInputStream;

        public FirebaseStorageFetcher(StorageReference ref) {
            mRef = ref;
        }

        @Override
        public void loadData(@NonNull Priority priority,
                             @NonNull final DataCallback<? super InputStream> callback) {
            mStreamTask = mRef.getStream();
            mStreamTask
                    .addOnSuccessListener(snapshot -> {
                        mInputStream = snapshot.getStream();
                        callback.onDataReady(mInputStream);
                    })
                    .addOnFailureListener(e -> callback.onLoadFailed(e));
        }

        @Override
        public void cleanup() {
            // Close stream if possible
            if (mInputStream != null) {
                try {
                    mInputStream.close();
                    mInputStream = null;
                } catch (IOException e) {
                    Log.w(TAG, "Could not close stream", e);
                }
            }
        }

        @Override
        public void cancel() {
            // Cancel task if possible
            if (mStreamTask != null && mStreamTask.isInProgress()) {
                mStreamTask.cancel();
            }
        }

        @NonNull
        @Override
        public Class<InputStream> getDataClass() {
            return InputStream.class;
        }

        @NonNull
        @Override
        public DataSource getDataSource() {
            return DataSource.REMOTE;
        }
    }
}

Once you clean rebuild the project you will be able to use Glide Module to fetch and load image files from the cloud storage.

Now, you are ready to test firebase storage.

package com.codeexa.com.firebasedatabaseexample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;

import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;

public class StorageMainActivity extends AppCompatActivity {

    private static final String TAG = "CodeExa.Com";


    private final String IMAGE_URL = "REPLACE_WITH_STORAGE_URL/Firebase.png";
    private ImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_main);

        mImageView = findViewById(R.id.imageView);

        fetchData();
    }

    private void fetchData() {
        StorageReference ref = FirebaseStorage.getInstance().getReferenceFromUrl(IMAGE_URL);

        GlideApp.with(this)
                .load(ref)
                .into(mImageView);
    }
}

Layout file

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:contentDescription="Firebase Storage"
        android:scaleType="fitCenter"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

You can similarly create or delete firebase storage files. Check out the next article to understand how to create new storage file. You will also learn how to delete from firebase database and storage.

Leave a Reply