Kotlin Firebase Storage – Download Files to Memory, Local File | Android

In previous post, we had known the way to upload data. This tutorial shows you how to download Files to Memory, Local File with Firebase Cloud Storage.

Related Posts:
Kotlin Firebase Storage – Upload Data from Memory, Local File, Stream | Android
Kotlin Firebase Storage – Get List of Files example – Image List with FirebaseRecyclerAdapter | Android

I. How to download file

To use the Firebase Storage to download file, we need:
– add Firebase to Android App & enable Firebase Auth
– create a reference to the full path of the file, including the file name
– download file using getBytes() for in-memory data, getFile() for local file.

To know way to add Firebase Storage to Android App & enable Firebase Auth, please visit previous post:
Kotlin Firebase Storage – Upload Data from Memory, Local File, Stream | Android

This tutorial bases on the code from that post, and continues with download methods.

1. Create a Reference


// From our app
val storageRef = storage.getReference()

// With an initial file path and name
val pathReference = storageRef.child("images/javasampleapproach.jpg")

// To a file from a Google Cloud Storage URI
val gsReference = storage.getReferenceFromUrl("gs://javasampleapproach-storage.appspot.com/images/javasampleapproach.jpg")

// From an HTTPS URL
val httpsReference = storage.getReferenceFromUrl("https://firebasestorage.googleapis.com/v0/b/javasampleapproach-storage.appspot.com/o/images%2Fjavasampleapproach.jpg")

2. Download Data using

2.1 getBytes()


// private var fileRef : StorageReference? = ...

val ONE_MEGABYTE = (1024 * 1024).toLong()
fileRef.getBytes(ONE_MEGABYTE)
        .addOnSuccessListener { bytes ->
            // Data is returned in bytes array
        }
        .addOnFailureListener { exception ->
            // Handle any error
        }

getBytes() method is the easiest way to download a file, it must load the entire into memory.
If we request a file larger than available memory, the app will crash. So we should set the maximum size, or use another download method.

2.2 getFile()

When we wanna access the file while offline or to share the file in a different app, we use getFile(). It returns a DownloadTask to manage download and monitor the status of the download:


// private var fileRef : StorageReference? = ...

val localFile: File = File.createTempFile("images", "jpg")
fileRef.getFile(localFile)
        .addOnSuccessListener { taskSnapshot ->
            // Local temp file has been created
            // use localFile
        }
        .addOnFailureListener { exception ->
            // Handle any errors
        }
        .addOnProgressListener { taskSnapshot ->
            // taskSnapshot.bytesTransferred
            // taskSnapshot.totalByteCount
        }

II. Practice

1. Goal

We will build an Android App that can:
– create Account, sign in/sign out for Firebase Authentication.
– choose image from Gallery, then upload it to Firebase Cloud Storage using putBytes(), putStream() and putFile() methods.
(previous post’s functions)
– download file (image) that is uploaded recently using getBytes(), getFile() methods, then display it.

kotlin-firebase-storage-download-file-demo-app

2. Technology

– Gradle 3.0.1
– Android Studio 3.x
– Firebase Android SDK 11.x

3. Project Structure

kotlin-firebase-storage-download-file-structure
LoginActivity is for Authentication, then user can change to StorageActivity to upload/download image to Firebase Cloud Storage.

4. Step by step

4.1 Create Android Project

– Generate new Android Project with package com.javasampleapproach.kotlin.firebase.storage.
– Follow steps to add Firebase Auth and Firebase Storage.

4.2 Enable Firebase Auth

Go to Your Firebase Project Console -> Authentication -> SIGN-IN METHOD -> Enable Email/Password.

4.3 LoginActivity

In this tutorial, we don’t explain way to authenticate an user again. To know how to implement Firebase Authentication App Client, please visit:
Kotlin Firebase Authentication – How to Sign Up, Sign In, Sign Out, Verify Email | Android

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="3">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:gravity="center"
            android:text="ozenero.com"
            android:textSize="28sp" />

        <TextView
            android:id="@+id/tvStatus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="4dp"
            android:text="Signed Out"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tvDetail"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="4dp"
            android:textSize="14sp"
            tools:text="Firebase User ID: 123456789abc" />

    </LinearLayout>


    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#E0E0E0"
        android:gravity="center_vertical">

        <LinearLayout
            android:id="@+id/email_password_fields"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

            <EditText
                android:id="@+id/edtEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Email"
                android:inputType="textEmailAddress" />

            <EditText
                android:id="@+id/edtPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Password"
                android:inputType="textPassword" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/email_password_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/email_password_fields"
            android:orientation="horizontal"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

            <Button
                android:id="@+id/btn_email_sign_in"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1"
                android:text="Sign In" />

            <Button
                android:id="@+id/btn_email_create_account"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1"
                android:text="Create Account" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_signed_in_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="horizontal"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:visibility="gone"
            android:weightSum="2.0">

            <Button
                android:id="@+id/btn_sign_out"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1.0"
                android:text="Sign Out" />

            <Button
                android:id="@+id/btn_test_storage"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1.0"
                android:text="Test Storage" />

        </LinearLayout>

    </RelativeLayout>

</LinearLayout>

package com.javasampleapproach.kotlin.firebase.storage

import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_login.*
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import android.widget.Toast
import android.util.Log
import android.text.TextUtils
import android.content.Intent
import android.support.v7.app.AppCompatActivity


class LoginActivity : AppCompatActivity(), View.OnClickListener {

    private val TAG = "LoginActivity"

    private var mAuth: FirebaseAuth? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        btn_email_sign_in.setOnClickListener(this)
        btn_email_create_account.setOnClickListener(this)
        btn_sign_out.setOnClickListener(this)
        btn_test_storage.setOnClickListener(this)

        mAuth = FirebaseAuth.getInstance()
    }

    override fun onStart() {
        super.onStart()

        val currentUser = mAuth!!.currentUser
        updateUI(currentUser)
    }

    override fun onClick(view: View?) {
        val i = view!!.id

        when (i) {
            R.id.btn_email_create_account -> createAccount(edtEmail.text.toString(), edtPassword.text.toString())
            R.id.btn_email_sign_in -> signIn(edtEmail.text.toString(), edtPassword.text.toString())
            R.id.btn_sign_out -> signOut()
            R.id.btn_test_storage -> testStorage()
        }
    }

    private fun createAccount(email: String, password: String) {
        Log.e(TAG, "createAccount:" + email)
        if (!validateForm(email, password)) {
            return
        }

        mAuth!!.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        Log.e(TAG, "createAccount: Success!")

                        // update UI with the signed-in user's information
                        val user = mAuth!!.currentUser
                        updateUI(user)
                    } else {
                        Log.e(TAG, "createAccount: Fail!", task.exception)
                        Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show()
                        updateUI(null)
                    }
                }
    }

    private fun signIn(email: String, password: String) {
        Log.e(TAG, "signIn:" + email)
        if (!validateForm(email, password)) {
            return
        }

        mAuth!!.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        Log.e(TAG, "signIn: Success!")

                        // update UI with the signed-in user's information
                        val user = mAuth!!.currentUser
                        updateUI(user)
                    } else {
                        Log.e(TAG, "signIn: Fail!", task.exception)
                        Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show()
                        updateUI(null)
                    }

                    if (!task.isSuccessful) {
                        tvStatus.text = "Authentication failed!"
                    }
                }
    }

    private fun signOut() {
        mAuth!!.signOut()
        updateUI(null)
    }

    private fun validateForm(email: String, password: String): Boolean {

        if (TextUtils.isEmpty(email)) {
            Toast.makeText(applicationContext, "Enter email address!", Toast.LENGTH_SHORT).show()
            return false
        }

        if (TextUtils.isEmpty(password)) {
            Toast.makeText(applicationContext, "Enter password!", Toast.LENGTH_SHORT).show()
            return false
        }

        if (password.length < 6) {
            Toast.makeText(applicationContext, "Password too short, enter minimum 6 characters!", Toast.LENGTH_SHORT).show()
            return false
        }

        return true
    }

    private fun updateUI(user: FirebaseUser?) {

        if (user != null) {
            tvStatus.text = "User Email: " + user.email
            tvDetail.text = "Firebase User ID: " + user.uid

            email_password_buttons.visibility = View.GONE
            email_password_fields.visibility = View.GONE
            layout_signed_in_buttons.visibility = View.VISIBLE
        } else {
            tvStatus.text = "Signed Out"
            tvDetail.text = null

            email_password_buttons.visibility = View.VISIBLE
            email_password_fields.visibility = View.VISIBLE
            layout_signed_in_buttons.visibility = View.GONE
        }
    }

    private fun testStorage() {
        startActivity(Intent(this, StorageActivity::class.java))
    }
}

4.4 StorageActivity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="16dp"
    android:paddingRight="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="ozenero.com"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="3">

        <Button
            android:id="@+id/btn_choose_file"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Choose File" />

        <EditText
            android:id="@+id/edtFileName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="File Name" />

    </LinearLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="Upload using:"
        android:textSize="18sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="3">

        <Button
            android:id="@+id/btn_upload_byte"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Bytes" />

        <Button
            android:id="@+id/btn_upload_file"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="File" />

        <Button
            android:id="@+id/btn_upload_stream"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Stream" />

    </LinearLayout>

    <TextView
        android:id="@+id/tvFileName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_margin="5dp"
        android:text="File Name" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="Download using:"
        android:textSize="18sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="2">

        <Button
            android:id="@+id/btn_download_byte"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Bytes" />

        <Button
            android:id="@+id/btn_download_file"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="File" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:weightSum="5">

        <ImageView
            android:id="@+id/imgFile"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="4.7" />

        <Button
            android:id="@+id/btn_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Back" />

    </LinearLayout>

</LinearLayout>

package com.javasampleapproach.kotlin.firebase.storage

import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.provider.MediaStore
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_storage.*
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.webkit.MimeTypeMap

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

import android.widget.Toast
import android.graphics.BitmapFactory
import java.io.*


class StorageActivity : AppCompatActivity(), View.OnClickListener {

    private val TAG = "StorageActivity"
    //track Choosing Image Intent
    private val CHOOSING_IMAGE_REQUEST = 1234

    private var fileUri: Uri? = null
    private var bitmap: Bitmap? = null
    private var imageReference: StorageReference? = null
    private var fileRef : StorageReference? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_storage)

        tvFileName.text = ""

        imageReference = FirebaseStorage.getInstance().reference.child("images")

        btn_choose_file.setOnClickListener(this)
        btn_upload_byte.setOnClickListener(this)
        btn_upload_file.setOnClickListener(this)
        btn_upload_stream.setOnClickListener(this)
        btn_back.setOnClickListener(this)

        btn_download_byte.setOnClickListener(this)
        btn_download_file.setOnClickListener(this)
    }

    override fun onClick(view: View?) {
        val i = view!!.id

        when (i) {
            R.id.btn_choose_file -> showChoosingFile()
            R.id.btn_upload_byte -> uploadBytes()
            R.id.btn_upload_file -> uploadFile()
            R.id.btn_upload_stream -> uploadStream()
            R.id.btn_back -> finish()
            R.id.btn_download_byte -> downloadInMemory(fileRef)
            R.id.btn_download_file -> downloadToLocalFile(fileRef)
        }
    }

    private fun uploadBytes() {
        // ... fileRef = imageReference!!.child(fileName + "." + getFileExtension(fileUri!!))
    }

    private fun uploadFile() {
        // ... fileRef = imageReference!!.child(fileName + "." + getFileExtension(fileUri!!))
    }

    private fun uploadStream() {
        // ... fileRef = imageReference!!.child(fileName + "." + getFileExtension(fileUri!!))
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (bitmap != null) {
            bitmap!!.recycle()
        }

        if (requestCode == CHOOSING_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.data != null) {
            fileUri = data.data
            try {
                bitmap = MediaStore.Images.Media.getBitmap(contentResolver, fileUri)
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    private fun downloadInMemory(fileRef: StorageReference?) {
        if (fileRef != null) {
            tvFileName.text = "Downloading..."

            val ONE_MEGABYTE = (1024 * 1024).toLong()
            fileRef.getBytes(ONE_MEGABYTE)
                    .addOnSuccessListener { bytes ->
                        val bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                        imgFile.setImageBitmap(bmp)
                        tvFileName.text = fileRef.name
                    }
                    .addOnFailureListener { exception ->
                        tvFileName.text = ""
                        Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
                    }
        } else {
            Toast.makeText(this, "Upload file before downloading", Toast.LENGTH_LONG).show()
        }
    }

    private fun downloadToLocalFile(fileRef: StorageReference?) {
        if (fileRef != null) {
            tvFileName.text = "Downloading..."

            try {
                val localFile: File = File.createTempFile("images", "jpg")

                fileRef.getFile(localFile)
                        .addOnSuccessListener {
                            val bmp = BitmapFactory.decodeFile(localFile.absolutePath)
                            imgFile.setImageBitmap(bmp)
                            tvFileName.text = fileRef.name
                        }
                        .addOnFailureListener { exception ->
                            tvFileName.text = ""
                            Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
                        }
                        .addOnProgressListener { taskSnapshot ->
                            // progress percentage
                            val progress = 100.0 * taskSnapshot.bytesTransferred / taskSnapshot.totalByteCount

                            // percentage in progress
                            val intProgress = progress.toInt()
                            tvFileName.text = "Downloaded " + intProgress + "%..."
                        }
            } catch (e: IOException) {
                e.printStackTrace()
            }

        } else {
            Toast.makeText(this, "Upload file before downloading", Toast.LENGTH_LONG).show()
        }
    }

    private fun showChoosingFile() {
        val intent = Intent()
        intent.type = "image/*"
        intent.action = Intent.ACTION_GET_CONTENT
        startActivityForResult(Intent.createChooser(intent, "Select Image"), CHOOSING_IMAGE_REQUEST)
    }

    private fun getFileExtension(uri: Uri): String {
        val contentResolver = contentResolver
        val mime = MimeTypeMap.getSingleton()

        return mime.getExtensionFromMimeType(contentResolver.getType(uri))
    }

    private fun validateInputFileName(fileName: String): Boolean {
        if (TextUtils.isEmpty(fileName)) {
            Toast.makeText(this, "Enter file name!", Toast.LENGTH_SHORT).show()
            return false
        }

        return true
    }
}

4.5 Android Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.javasampleapproach.kotlin.firebase.storage">

    <application ...>
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".StorageActivity"></activity>
    </application>

</manifest>

4.6 Run & Check result

- Use Android Studio, build and Run your Android App.

- Upload a file first, then click any Download button:

kotlin-firebase-storage-download-file-demo-app

III. Source code

Kotlin-FirebaseStorage-Download-File

Leave a Reply

Your email address will not be published. Required fields are marked *