Getting Cracking with Firebase Cloud Storage on Android

http://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pnghttp://justmobiledev.com/wp-content/uploads/2017/12/firebasestorage-android-title-1.pngGetting Cracking with Firebase Cloud Storage on Android

Here a quick and hopefully not too dirty tutorial on how to get started with Firebase Cloud Storage (FCS) on Android.

Firebase Cloud Storage Concepts

First, let’s review a couple of concepts that you want to understand before using FCS:

Projects

All data in cloud storage is associated (owned) by a project. A project consists of a set of users, a set of APIs, and billing, authentication, and monitoring settings for those APIs. One can create one project or multiple projects.

Buckets

Buckets are containers that hold your everything you want to store. All data must be contained in a bucket. Data can be organized into different buckets with separate data access control rules but unlike directories, buckets cannot be nested.

Objects

Objects are the individual pieces of data that are stored in Cloud Storage. Objects have two components: object data and object metadata. The object data component is usually a file to be stored. The object metadata component is a collection of name-value pairs that describe various object qualities, such as name, file type, etc. There is no limit on the number of objects that can be placed in a bucket.

References

A Reference is a pointer to a file hosted in Firebase Storage. It is the handle you can use to upload or download a file or get it’s meta data. A Reference can also be used to navigate, by using the getParent() and getRoot() methods.

Android Setup

First, create a Single Page Project in Android. Alternatively, you can download the Starter Project from my GitHub site and follow along.

Android Permissions
First you want to add a permission for the app to access the internet. This is done in the AndroidManifest.xml. The permissions node is a child of ‘manifest’ (not application).

1
2
3
4
5
6
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.mobile.justmobiledev.firebasestoragestarter" >
    <!-- PERMISSIONS -->
    <uses-permission android:name="android.permission.INTERNET" />
    <application>
...

Adding Dependencies

In your project gradle file (not application gradle), add the dependency for the google services plugin:

1
2
3
4
5
6
7
8
9
buildscript {
    repositories {
        ...
    }
    dependencies {
        ...
        classpath 'com.google.gms:google-services:3.1.1' // google-services plugin
    }
}

In the same file, add the Google Maven repository in the repositories section:

1
2
3
4
5
6
7
8
allprojects {
    repositories {
        ...
        maven {
            url "https://maven.google.com" // Google's Maven repository
        }
    }
}

First, add the dependency for firebase cloud storage to your application gradle file:

1
2
3
4
5
dependencies {
    ...
    compile 'com.google.firebase:firebase-storage:11.8.0'
    ...
}

At the end of your module build grade file, add the statement to apply the Google plugin:

1
2
// At the bottom of the file
apply plugin: 'com.google.gms.google-services'

Go ahead and sync your gradle (Tools / Android / Sync Project With Gradle Files).

Firebase Setup

If you haven’t already, register for a Firebase account. As of Feb 2016, you are required to use a Google gmail account to log in to Firebase, so if you haven’t already, go ahead and get a Gmail account.

Create a new Firebase Project and call it ‘FirebaseProjectStarter’ or select your own name.

Once you click on the project, you should see the project view. On the left navigation bar, click on ‘Develop’ and ‘Storage’ and then the ‘Get Started’ button.

You should now see the Storage View without any buckets or files created at the moment.

Let’s go ahead and create an Android application for your Firebase Project. On the left navigation bar, select the gear icon an then ‘Project Settings’.

Next, go ahead and click on the ‘Android’ button (Add Firebase to your Android appp). On the Android app pop-up, fill out the app package name and a nick name.

NOTE: You want to make sure that your package name you enter in Firebase matches your actual Android project package name exactly. If you are not sure what your project name is, check in your Android project (e.g. under app/java).

In the next step, you can download your google-services.json, and add it to your Android project. This file contains your Firebase API Key and information about your Firebase setup, such as your Firebase storage URL. So download the json file, open Android Studio, switch to Project View an drag & drop the google-services.json under the app directory (your project root).

Setting File Access Permissions
Before we can start using the Default bucket in Firebase Storage, we have to modify the Firebase access rules. So ideally you want to come up with file access rules to accommodate your application, e.g. to restrict file access to only authenticated users, but for the purpose of this tutotorial, we will set rules to open access.

In the Firebase Console, switch to the rules tab and set the following rule:

1
2
3
4
5
6
7
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

This means, every user has read/write access to every bucket.

Uploading a File

First let’s create a file to upload. Normally you want to probably upload a file from the apps photo/video folder, external storage, or documents directory. For simplicity, we are going to upload an image file from the app assets folder.

First, create the assets folder in Android Studio: Right-click on the app folder in Android view / New … / Folder … / Assets Folder.
Under the assets folder you can create a sub directory, e.g. ‘images’.

Next, drag & drop an image file from your explorer window into the project assets/images folder:

You can also upload a file from data in memory as byte array, a file that is locally stored on the device, or a file from Stream. For more information, please refer to the Firebase guide.

NOTE: When selecting an emulator to run the app, make sure you select an emulator that has a recent version of Google Play Services installed. Make sure you can launch Google Play on the device without error.

Let’s add a new method for uploading the file.
1. We are getting a reference to the FirebaseStorage singleton
2. We are getting a reference to the storage container
3. We are creating a new storage reference that points to ‘images/test.jpg’.
4. We are using the Android Asset Manager to open the test.jpg as an InputStream
5. We are using the reference.putStream() method to upload the file to the cloud
6. We register failure/success listeners to handle any results from the upload.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    private void uploadFile()
    {
        try {
            // Create Reference
            FirebaseStorage storage = FirebaseStorage.getInstance();

            // Create a storage reference from our app
            StorageReference storageRef = storage.getReference();

            String fileName = "images/test.jpg";

            // Child references can also take paths
            StorageReference testRef = storageRef.child(fileName);

            Log.d(TAG, "Path: "+testRef.getPath());
            Log.d(TAG, "Name: "+testRef.getName());
            Log.d(TAG, "Bucket: "+testRef.getBucket());

            AssetManager assetManager = getAssets();
            InputStream testFileStream = assetManager.open(fileName);

            // Upload to Firebase
            UploadTask uploadTask = testRef.putStream(testFileStream);
            uploadTask.addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception ex) {
                    // Handle unsuccessful uploads
                    Log.e(TAG, "An error occurred during upload: "+ex.getMessage());
                }
            }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                    Log.e(TAG, "Successful upload");
                }
            });
        }
        catch(Exception ex)
        {
            Log.e(TAG, ex.getMessage());
        }
    }

So go ahead and test it out. If you see a statement ‘Successful upload’ in the Android logcat, you know that up just uploaded your first file. You can then go to your Firebase Console, refresh the page and you should see the new test file under the image directory:

Uploading Bytes

If you’d like to upload a file from memory, you can use the reference.putBytes() method, which takes a Byte array as input.
Using this method, the new upload method would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    private void uploadFileBytes()
    {
        try {
            // Create Reference
            FirebaseStorage storage = FirebaseStorage.getInstance();

            // Create a storage reference from our app
            StorageReference storageRef = storage.getReference();

            String fileName = "images/test.jpg";

            // Child references can also take paths
            StorageReference testRef = storageRef.child(fileName);

            AssetManager assetManager = getAssets();
            InputStream testFileStream = assetManager.open(fileName);
            byte[] data = new byte[testFileStream.available()];
            DataInputStream dataInputStream = new DataInputStream(testFileStream);

            // Read stream into byte array
            dataInputStream.readFully(data);
            dataInputStream.close();

            // Upload to Firebase
            UploadTask uploadTask = testRef.putBytes(data);
            uploadTask.addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception ex) {
                    // Handle unsuccessful uploads
                    Log.e(TAG, "An error occurred during upload: "+ex.getMessage());
                }
            }).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                    Log.e(TAG, "Successful upload");
                }
            });
        }
        catch(Exception ex)
        {
            Log.e(TAG, ex.getMessage());
        }
    }

Getting File Meta Data

The process to get file information from a file stored in the cloud is similar to uploading a file:

1. Get a reference to Firebase Storage singleon
2. Get a reference to the actual file path
3. Execute the getMetadata() method on the file reference
4. Register Success/Failure handlers.

You can then use the Sucess handler to get meta data about the file, such as content type, file size or the download path.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   private void getFileMetaData(){
        // Create a storage reference from our app
        FirebaseStorage storage = FirebaseStorage.getInstance(); // 1
        StorageReference storageRef = storage.getReference();

        // Get reference to the file
        StorageReference testRef = storageRef.child("images/test.jpg"); // 2

        testRef.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() { // 3
            @Override
            public void onSuccess(StorageMetadata storageMetaData) { // 4
                Log.d(TAG, "Bucket: "+storageMetaData.getBucket());
                Log.d(TAG, "ContentType: "+storageMetaData.getContentType());
                Log.d(TAG, "Name: "+storageMetaData.getName());
                Log.d(TAG, "getPath: "+storageMetaData.getPath());
                Log.d(TAG, "SizeBytes: "+storageMetaData.getSizeBytes());
                Log.d(TAG, "DownloadUrl: "+storageMetaData.getDownloadUrl());
            }
        }).addOnFailureListener(new OnFailureListener() { // 4
            @Override
            public void onFailure(@NonNull Exception exception) {
                Log.e(TAG, ex.getMessage());
            }
        });
    }

Downloading a File

You can use the following approach to download a file hosted in the cloud locally to your device:
1. Get the FirebaseStorage singleton
2. Get a reference to the Storage
3. Create a reference to the file to be downloaded
4. Create a local file where the cloud file will be downloaded to.
5. Use the file reference.getFile() method with the local file reference to get the file
6. Register Success/Failure handlers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    private void downloadFile()
    {
        try {
            // Create Reference
            FirebaseStorage storage = FirebaseStorage.getInstance(); // 1

            // Create a storage reference from our app
            StorageReference storageRef = storage.getReference(); // 2

            String fileName = "images/test.jpg";

            // Child references can also take paths
            StorageReference testFileRef = storageRef.child(fileName); // 3

            File localFile = File.createTempFile("test", "jpg"); // 4

            testFileRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { // 5
                @Override
                public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { // 6
                    // Local temp file has been created
                    Log.d(TAG, "Successfully downloaded");
                }
            }).addOnFailureListener(new OnFailureListener() { // 6
                @Override
                public void onFailure(@NonNull Exception ex) {
                    // Handle any errors
                    Log.e(TAG, ex.getMessage());
                }
            });
        }
        catch(Exception ex)
        {
            Log.e(TAG, ex.getMessage());
        }
    }

That’s all I wanted to show in this FirebaseStorage starter tutorial. I hope it helps you get up to speed.

Cheers,

Chris

Author Description

justmobiledev

No comments yet.

Join the Conversation