Learn Firebase Cloud Storage Quickly [Guide]

Firebase Javascript

Firebase Storage is a great way to store all the asset files for your projects in a single place.

You can store pretty much any files such as:

  • images,
  • videos,
  • audio,
  • documents, etc

You can also create folders to organize other files or folders.

Uploading files and downloading file URLs are super easy and we can easily protect them using Firebase Storage Security Rules, which is very similar to Cloud Firestore Security Rules.

Table of Contents

Enable Firebase Storage

Enable Firebase Storage by clicking the Get Started button after logging into the Firebase account.

The default Cloud StorageSecurity Rule will be set to “only authenticated users can read and write data”,

Click next.

Then, it gives us a warning saying that once the Firebase Cloud Storage bucket is created, the physical location of it can’t be changed.

A Cloud Storage bucket stores all of our files and it ties up to a physical location. For example (us-central from the screenshot above)

Firebase storage allows us to create multiple buckets with different locations.

Click done.

Choose An Image

Choose an image file from a user by attaching a change event to the input element with the type attribute set to file.

HTML

<input type="file" onchange="uploadImage(e)" />

JavaScript

function uploadImage(e) {
  const file = e.target.files[0]
  console.log(file);
}

Then, we can have access to the actual file and its information using the event object e.target.files[0]

Upload An Image

To upload a file to the Firebase Cloud Storage, we need two pieces of information:

  • The location path that we want to upload a file into, and
  • The actual File

We can specify the location path as an argument to the ref() method.

firebase
    .storage()
    .ref('images/' + file.name)
    .put(file);

This will create a folder called images, if it could not find one, and store the actual file inside it with the file name mentioned in the location path after the slash (/).

Then, the put() method uploads the file that is passed into as an argument to the given location path.

There is another way of specifying the file path, which is using child() method.

firebase
    .storage()
    .ref('images')
    .child(file.name)
    .put(file);

The above code does exactly the same thing as the previous example.

This won’t work if you test it out at this point.

This is because

By default, Firebase Cloud Storage has security rules in place that can ONLY allow logged-in users to read and write data. 🔒

Let’s change that.

Go to the Storage Rules tab and change the line from

allow read, write: if request.auth.uid != null;

To

allow read, write: if true;

🛑 Warning: The above security is actually allowing anyone to read and write to the Cloud Storage bucket. I use this for demonstation purposes ONLY.

Multiple Storage Buckets

We can also create multiple buckets with different locations.

If you use multiple storage buckets, you will have to explicitly pass the bucket URL to the storage() method as an argument.

firebase
    .app()
    .storage('gs://your-project-name.appspot.com/')
    .ref('images')
    .child(file.name)
    .put(file);

To get the bucket URL, go to Storage Files URL (can be found at the top left).

Get An Image URL

To get a single file, specify the path with the folder and file names inside the ref() method and run getdownloadURL() method on it.

firebase
  .storage()
  .ref('images/golden-retriever.jpg')
  .getDownloadURL()
  .then(imgUrl => {
    console.log(imgUrl);
  });

The getDownloadURL() method will return a promise, and the actual file URL will be returned to the then() callback function specified in the parameter imgUrl.

Upload Multiple Files

To upload multiple images at once, add the multiple attribute to the input element.

HTML

<input type="file" onchange="uploadMultipleImages(e)" multiple />

Then, loop through the files object, which is a FileList object not an actual JavaScript array. So I am using for of to iterate over and upload it to the Cloud Storage.

forEach() won’t work as they are not an acutal array and you can convert it to an array like this: Array.prototype.slice.call(files)

JavaScript

function uploadMultipleImages(e) {
  let files = e.target.files;

  for (const file of files) {
    firebase
      .storage()
      .ref('images')
      .child(file.name)
      .put(file);
  }
}

Get All Images

The listAll() method will get all the file URLs in a given location path.

firebase
    .storage()
    .ref('images')
    .listAll()
    .then(snap => {
      snap.items.forEach(itemRef => {
        itemRef.getDownloadURL().then(imgUrl => {
          console.log(imgUrl)
        });
      })
    })

Inside the then() callback function, loop through the items array on the snapshot object.

Then, call getDownloadURL() method on the itemRef object which returns the actual file URLs inside the then() callback function again specified in the parameter imgUrl.

Delete A File

Find the location path of a file and delete it using delete() method.

firebase
  .storage()
  .ref('images/golden-retriever.jpg')
  .delete()
  .then(function() {
    console.log('File deleted successfully'); 
 }).catch(function(error) {
  console.log('error occured');
});

Firebase Storage With Authentication Demo

In this section, you’re going to learn how to upload a profile image with Authentication.

To get this working, I am going to split this into FOUR parts

Create A New User Account

Enable Email/Password sign-in method: Go to Authentication Tab → Sign-in Method → Choose Email/Password and Enable it.

index.html

Here is the simple signup form that has four inputs:

  • email,
  • password,
  • file, and
  • signup button

I also have an image element at the bottom to show the profile image once it’s uploaded to the Cloud Storage.

<!Doctype html>
<head>
  <title>Learn Firebase Storage Quickly</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.css" />
</head>
<body>
  <div class="ui two column middle aligned center aligned grid">
    <div class="column">
      <form class="ui large form">
        <div class="ui stacked secondary segment" >
          <div class="field">
            <div class="ui left icon input large">
              <i class="user icon"></i>
              <input type="text" placeholder="E-mail address" id="email" />
            </div>
          </div>
          <div class="field">
            <div class="ui left icon input large">
              <i class="lock icon"></i>
              <input type="text" placeholder="Password" id="pword" />
            </div>
          </div>
          <div class="field">
            <div class="ui left icon input large">
              <i class="image icon"></i>
              <input type="file" id="fileUploader" />
            </div>
          </div>
          <div class=" ui fluid large teal submit button" onclick="signUpUser()">Sign Up</div>
        </div>
        <div class="ui large image">
          <img id="img">
        </div>
      </form>
    </div>
  </div>
  <script src="https://www.gstatic.com/firebasejs/7.9.0/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.9.0/firebase-auth.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.9.0/firebase-storage.js"></script>
  <script src="app.js"></script>
</body>
</html>

I use the Semantic-UI CSS framework for this example.

At the bottom, make sure to add the following Firebase SDKs:

  • App
  • Authentication, and
  • Storage.

⚠️ The latest version of Firebase SDK has some issues with CORS atleast at time of this writing. So make sure to use the Firebase SDK version of 7.9.0 to avoid a CORS issue.

In the JavaScript file, replace the firebaseConfig code with yours. You can find it at Firebase Project Overview at the top ⚙ → Project Setting Register App

app.js

var firebaseConfig = {
  apiKey: "*****************",
  authDomain: "*****************",
  databaseURL: "*****************",
  projectId: "*****************",
  storageBucket: "*****************",
  messagingSenderId: "*****************",
  appId: "*****************",
  measurementId: "*****************"
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);

const email = document.getElementById('email'),
  pword = document.getElementById('pword'),
  fileUploader = document.getElementById('fileUploader');

let file = {};

fileUploader.addEventListener('change', function (e) {
  file = e.target.files[0];
})

function signUpUser() {
  firebase.auth().createUserWithEmailAndPassword(email.value, pword.value).then(auth => {
    console.log(auth)
  }).catch(error => {
    console.log(error.message)
  })
}

Once a new user is created, an auth object will be returned to the then() callback function specified in the parameter auth.

Firebase Storage Structure & Security Rules

Before uploading a file to the storage, let’s structure the files in a way that only authenticated users can read and write.

To do that, I am going to create a folder called users then create another folder using the user’s UID as a folder name. Then, store the file in there with the fixed name called profile.jpg.

Assuming I will be only uploading .jpg files just for the simplicity sake.

Go to Firebase ConsoleStorage Section → Choose Rules Tab from the top.

Then, add the following security rule code in there.

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{uid}/{profileImage} {
      allow read, write: if request.auth.uid == uid;
    }
  }
}

The Cloud Storage security rule below gives any user read and write permission to the location path ( /users/{uid}/{profileImage}), as long as the logged-in user’s UID (request.auth.uid) matches with the uid which is referring to the place holder {uid} mentioned in the location path.

Upload Logged-In User’s Profile Picture

Once a new user account is created, inside the then() callback function, upload the profile picture with the user’s UID.

function signUpUser() {
  firebase.auth().createUserWithEmailAndPassword(email.value, pword.value).then(auth => {
    firebase
      .storage()
      .ref("users")
      .child(auth.user.uid + "/profile.jpg")
      .put(file);

  }).catch(error => {
    console.log(error.message)
  })
}

And the file structure should be something like this in the Firebase Storage Dashboard.

Get Logged-In User’s Profile Picture

Invoke onAuthStateChange() method to check if any user is logged in.

If there is a user, then get the profile picture by calling getDowloadURL() method.

Inside the then() callback function, the actual image URL specified in the parameter called imgURL.

Then, set it to the img element to display it on the browser.

const img = document.getElementById('img');

firebase.auth().onAuthStateChanged(user => {
  if (user) {
    firebase
      .storage()
      .ref("users")
      .child(user.uid + "/profile.jpg")
      .getDownloadURL()
      .then(imgUrl => {
        img.src = imgUrl;
      });
    console.log(user)
  }
})

There you have it.

Find the full source code on Github.

If you have any suggestions, feedback or if anything is unclear in this article, please reach out to me by commenting below.

I am looking forward to hearing from you and Happy Coding!