Firebase

Firebase – Storage

Hello droiders, welcome to the third part of this tutorial series on Firebase. A quick glimpse of previous parts:

  • Part 1, Firebase – Cloud Messaging, formerly known as Google Cloud Messaging, is a new and improved way of delivering Push Notifications to major platforms that includes Android, iOS and web.
  • Part 2, Firebase – Crash Reporting, a comprehensive tool to receive detailed information about the Crash.

In this part

Now today in this part we are going to talk about “Firebase – Storage”. While developing applications, almost all the junior or intermediate developers has the same nightmare “How to upload a file?”. You might be messing up around how to upload a file to our web server. For this, web APIs development team will try to create a web api for you and you will consume it and blah blah blah…

But then now there are different scenarios we have keep in mind while writing down the code for this file uploading task, some of them are:

  • What if network is lost ?
  • What if connection speed is slow ?
  • What if file to be uploaded is large in size
  • Can uploading be paused and resumed ?
  • and what not..!!!

Now, you might have watched videos (or attended talks in presence) of Firebase sessions delivered in Google IO 2016, if not then this article is for you 🙂 There they have announced many features of Firebase and one of them is “Firebase – Storage”, and through which you got all of these problems solved and is where “Firebase – Storage” comes into picture.

Why to switch over it ?

As mentioned above there are different scenarios which we have to keep in mind while integrating APIs for accessing remote database. Here are some more points though which we can consider “Why to get started with Firebase”:

  • Firebase Storage is built for app developers like us, comes with storage backed with Google Cloud Storage
  • Its apis are insanely easy to use. You can store any user-generated contents such as Images, Audios, Videos or any other content to Storage
  • You can upload or download file regardless of Network.
  • They can be stopped and restarted anytime.
  • Seamless Uploads and downloads. You can pause and resume it.
  • and there are many more such…Share it in comment if you know 🙂

BTW, hope you are aware that “Snapchat” is built on this. So, lets hit the target and lets get started!

Getting started

In this post, we are going to create a demo that will perform following operations:

  • Connect to Firebase Storage
  • Create a directory named “images”
  • Upload a file in images directory
  • Download a file from images directory
  • Delete a file from images directory

Pre-requisites

  • Android Studio 1.5 or above
  • Google Play Services 9.0.2 or above.

Add Firebase to your app

    1. Go to Firebase console and click on “CREATE NEW PROJECT”. Fill the “Project Name” and Select Country, then click on “CREATE PROJECT”

      cropped1

 

    1. Click on “Add Firebase to your Android app”

      cropped2

 

    1. Enter Package name and Debug signing certificate SHA-1(Optional) and click on “Add app” button

      cropped3

    2. Google-services.json file will be downloaded. Go to Android Studio and Switch to the Project view in Android Studio to see your project root directory. Move the google-services.json file you just downloaded into your Android app module root directory.

      cropped4

    3. The Google services plugin for Gradle loads the google-services.json file you just downloaded. Modify your build.gradle files to use the plugin.

 

  1. Add the following lines, to your Project-level build.gradle (/build.gradle):
    buildscript {
        dependencies {
            // Add this line
            classpath 'com.google.gms:google-services:3.0.0'
        }
    }
    
  2. Add the following lines to the bottom of your App-level build.gradle:
    apply plugin: 'com.google.gms.google-services'
    
  3. Add the following dependencies, to your App-level build.gradle
    compile 'com.google.firebase:firebase-storage:9.0.2'
    compile 'com.google.firebase:firebase-auth:9.0.2'
    
  4. Finally, press “Sync now”.

Code

Before we start to code, we need the url to our storage where our files will be kept. So,

Go to Firebase console and Select your target project.

On the left side menu, click on “Storage”

Firebase Storage 1

You will see the screen as below:

Firebase Storage 2

You can see Url of the form

gs://**something**.appspot.com

Now, to skip the authentication portion of firebase, we have to set Rules to public in Rules tab.
You might see the rules as below:

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

Copy and replace the rules with below lines:

service firebase.storage {
  match /b/**something**.appspot.com/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

Caution: With above rules, your security rules will be set to public and anyone can read or write to your storage. Make sure you don’t share Url with anyone.

Ok lets move forward.

Create an activity “activity_main.xml” as below:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
    android:fitsSystemWindows="true"
    tools:context="com.example.ln_202.firebasestorage.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <LinearLayout
        android:id="@+id/content_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:padding="@dimen/activity_horizontal_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_weight="1"
            android:adjustViewBounds="true"
            android:src="@drawable/placeholder_image" />

        <Button
            android:id="@+id/button_step_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onCreateReferenceClick"
            android:text="Step 1: Create a Reference" />

        <Button
            android:id="@+id/button_step_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:onClick="onCreateDirectoryClick"
            android:text="Step 2: Create a directory" />

        <Button
            android:id="@+id/button_step_3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:onClick="onUploadFileClick"
            android:text="Step 3: Upload a file" />

        <Button
            android:id="@+id/button_step_4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:onClick="onDownloadFileClick"
            android:text="Step 4: Download a file" />

        <Button
            android:id="@+id/button_step_5"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:enabled="false"
            android:onClick="onDeleteFileClick"
            android:text="Step 5: Delete a file" />

    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

Create an activity “MainActivity.java” as below:


import android.Manifest;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.bumptech.glide.Glide;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.storage.FileDownloadTask;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.OnProgressListener;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;

import java.io.File;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_PICK_IMAGE = 1;
    private static final int PERMISSION_READ_WRITE_EXTERNAL_STORAGE = 2;

    private FirebaseStorage mFirebaseStorage;
    private StorageReference mStorageReference;
    private StorageReference mStorageReferenceImages;
    private Uri mUri;
    private ImageView mImageView;
    private ProgressDialog mProgressDialog;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        mImageView = (ImageView) findViewById(R.id.imageView);
        setSupportActionBar(toolbar);

        // Create an instance of Firebase Storage
        mFirebaseStorage = FirebaseStorage.getInstance();
    }

    private void pickImage() {
        Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            if (requestCode == REQUEST_CODE_PICK_IMAGE) {
                String filePath = FileUtil.getPath(this, data.getData());
                mUri = Uri.fromFile(new File(filePath));
                uploadFile(mUri);
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_READ_WRITE_EXTERNAL_STORAGE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                pickImage();
            }
        }
    }

    private void showProgressDialog(String title, String message) {
        if (mProgressDialog != null && mProgressDialog.isShowing())
            mProgressDialog.setMessage(message);
        else
            mProgressDialog = ProgressDialog.show(this, title, message, true, false);
    }

    private void hideProgressDialog() {
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
    }

    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    public void showHorizontalProgressDialog(String title, String body) {

        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.setTitle(title);
            mProgressDialog.setMessage(body);
        } else {
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setTitle(title);
            mProgressDialog.setMessage(body);
            mProgressDialog.setIndeterminate(false);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setProgress(0);
            mProgressDialog.setMax(100);
            mProgressDialog.setCancelable(false);
            mProgressDialog.show();
        }
    }

    public void updateProgress(int progress) {
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.setProgress(progress);
        }
    }

    /**
     * Step 1: Create a Storage
     *
     * @param view
     */
    public void onCreateReferenceClick(View view) {
        mStorageReference = mFirebaseStorage.getReferenceFromUrl("gs://**something**.appspot.com");
        showToast("Reference Created Successfully.");
        findViewById(R.id.button_step_2).setEnabled(true);
    }

    /**
     * Step 2: Create a directory named "Images"
     *
     * @param view
     */
    public void onCreateDirectoryClick(View view) {
        mStorageReferenceImages = mStorageReference.child("images");
        showToast("Directory 'images' created Successfully.");
        findViewById(R.id.button_step_3).setEnabled(true);
    }

    /**
     * Step 3: Upload an Image File and display it on ImageView
     *
     * @param view
     */
    public void onUploadFileClick(View view) {
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_READ_WRITE_EXTERNAL_STORAGE);
        else {
            pickImage();
        }
    }

    /**
     * Step 4: Download an Image File and display it on ImageView
     *
     * @param view
     */
    public void onDownloadFileClick(View view) {
        downloadFile(mUri);
    }

    /**
     * Step 5: Delete am Image File and remove Image from ImageView
     *
     * @param view
     */
    public void onDeleteFileClick(View view) {
        deleteFile(mUri);
    }

    private void showAlertDialog(Context ctx, String title, String body, DialogInterface.OnClickListener okListener) {

        if (okListener == null) {
            okListener = new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            };
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(ctx).setMessage(body).setPositiveButton("OK", okListener).setCancelable(false);

        if (!TextUtils.isEmpty(title)) {
            builder.setTitle(title);
        }

        builder.show();
    }

    private void uploadFile(Uri uri) {
        mImageView.setImageResource(R.drawable.placeholder_image);

        StorageReference uploadStorageReference = mStorageReferenceImages.child(uri.getLastPathSegment());
        final UploadTask uploadTask = uploadStorageReference.putFile(uri);
        showHorizontalProgressDialog("Uploading", "Please wait...");
        uploadTask
                .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                    @Override
                    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                        hideProgressDialog();
                        Uri downloadUrl = taskSnapshot.getDownloadUrl();
                        Log.d("MainActivity", downloadUrl.toString());
                        showAlertDialog(MainActivity.this, "Upload Complete", downloadUrl.toString(), new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                findViewById(R.id.button_step_3).setEnabled(false);
                                findViewById(R.id.button_step_4).setEnabled(true);
                            }
                        });

                        Glide.with(MainActivity.this)
                                .load(downloadUrl)
                                .into(mImageView);
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception exception) {
                        exception.printStackTrace();
                        // Handle unsuccessful uploads
                        hideProgressDialog();
                    }
                })
                .addOnProgressListener(MainActivity.this, new OnProgressListener<UploadTask.TaskSnapshot>() {
                    @Override
                    public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
                        int progress = (int) (100 * (float) taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount());
                        Log.i("Progress", progress + "");
                        updateProgress(progress);
                    }
                });
    }

    private void downloadFile(Uri uri) {
        mImageView.setImageResource(R.drawable.placeholder_image);
        final StorageReference storageReferenceImage = mStorageReferenceImages.child(uri.getLastPathSegment());
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "Firebase Storage");
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.d("MainActivity", "failed to create Firebase Storage directory");
            }
        }

        final File localFile = new File(mediaStorageDir, uri.getLastPathSegment());
        try {
            localFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

        showHorizontalProgressDialog("Downloading", "Please wait...");
        storageReferenceImage.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
                hideProgressDialog();
                showAlertDialog(MainActivity.this, "Download Complete", localFile.getAbsolutePath(), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        findViewById(R.id.button_step_4).setEnabled(false);
                        findViewById(R.id.button_step_5).setEnabled(true);
                    }
                });

                Glide.with(MainActivity.this)
                        .load(localFile)
                        .into(mImageView);
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception exception) {
                // Handle any errors
                hideProgressDialog();
                exception.printStackTrace();
            }
        }).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() {
            @Override
            public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) {
                int progress = (int) (100 * (float) taskSnapshot.getBytesTransferred() / taskSnapshot.getTotalByteCount());
                Log.i("Progress", progress + "");
                updateProgress(progress);
            }
        });
    }

    private void deleteFile(Uri uri) {
        showProgressDialog("Deleting", "Please wait...");
        StorageReference storageReferenceImage = mStorageReferenceImages.child(uri.getLastPathSegment());
        storageReferenceImage.delete().addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                hideProgressDialog();
                showAlertDialog(MainActivity.this, "Success", "File deleted successfully.", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        mImageView.setImageResource(R.drawable.placeholder_image);
                        findViewById(R.id.button_step_3).setEnabled(true);
                        findViewById(R.id.button_step_4).setEnabled(false);
                        findViewById(R.id.button_step_5).setEnabled(false);
                    }
                });
                File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                        Environment.DIRECTORY_PICTURES), "Firebase Storage");
                if (!mediaStorageDir.exists()) {
                    if (!mediaStorageDir.mkdirs()) {
                        Log.d("MainActivity", "failed to create Firebase Storage directory");
                    }
                }
                deleteFiles(mediaStorageDir);
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception exception) {
                hideProgressDialog();
                exception.printStackTrace();
            }
        });
    }

    private void deleteFiles(File directory) {
        if (directory.isDirectory())
            for (File child : directory.listFiles())
                child.delete();
    }
}

Add the Glide dependency in your module level “build.gradle(Module:app)” as below:

dependencies {
    ...
    compile 'com.github.bumptech.glide:glide:3.7.0'
}

And Run the project.

Output:

Screenshot_20160624-191752

Tadaa…You just created explored Firebase Storage with following functionalities:

Creating Firebase Reference

Screenshot_20160624-191659

Creating a Folder on Firebase Storage

Screenshot_20160624-191704

Upload a File

Screenshot_20160624-191732

Download a File

Screenshot_20160624-191759

Delete a File

Screenshot_20160624-191827

Conclusion

So, its damn easy to work with Firebase Storage, and yes, you might expect the whole working demo.

Here’s link: Firebase Storage Demo

Hope you enjoyed reading this article and learnt from it. You can expect a next part in this series very soon! Hope to see you again.

Want to work with us? We're hiring!
  • Ranjith

    Awesome Bro! Nicely explained. You made it look simple. 🙂

    • Chintan Soni

      Thanks and Yes.. thats what I love to do.. To make stuffs simple.. 🙂

  • Raghav Satyadev

    at last, found some very easy methods for file uploading, thank you chintan sir.

    • Chintan Soni

      Thanks Raghav.. 🙂

  • Hardik Gajera

    Nice..Really Usefull.

    • Chintan Soni

      thanks Hardik.. 🙂

  • Android Developer

    thats is gud work for me but can u tell me i all upload image URL in array list . .then i can show image one by one ????

  • Benedict Bensisakht

    Very nice! Thanks a lot. I notice you are using firebase dependencies of 9.0.2 but it seems there are newer versions now, in firebase website says 9.2.1 , although I have not been able to get it to work, i’m using 9.2.0. https://firebase.google.com/docs/storage/android/start Any thoughts?

  • Mayur Parekh

    Nice, Thanks.

    Any plan for posting on “Firbase Database” in nearest future ?