Firebase Storage – Upload, Download, and Delete

In this tutorial, we are going to teach you, how you can upload, download and delete files from Firebase Storage. Firebase Storage is a service provided by Google that allows you to save files on their server. The free plan allows you to upload data up to 1 GB. More data can be bought from the Firebase Pricing page.

What you are going to learn:

  1. Upload files on Firebase Storage.
  2. Save the data in Realtime Database.
  3. Fetch files from the Firebase Storage.
  4. Download files from Firebase Storage.
  5. Fix CORS issue if working in localhost.
  6. Install gsutil.
  7. Delete files from Firebase Storage.

Video tutorial:

Upload file

We are going to upload the file to the Firebase storage and save its path in Firebase Realtime Database. It is another service provided by Google to save data in JSON format. To upload the file, we are going to show a form with an input type file that allows the user to select any type of file. We will be using Vue JS to render the HTML to view the data in real-time, for example, newly uploaded files will be displayed automatically and deleted files will be removed from the view without having to refresh the page.

<div id="app">
	<form id="upload-form">
		<input type="file" name="file" required />
		<input type="submit" value="Upload" />
	</form>

    [show all uploaded here]
</div>

<script src="vue.min.js"></script>

This will display a form with an input type file to upload a file. And a submit button which when clicked will submit the form. Vue JS production file can be downloaded from here. Now you need to attach a submit event listener to this form using Javascript.

<script type="module">

	// [initialize firebase here]

	window.addEventListener("load", function () {
		document.getElementById("upload-form").addEventListener("submit", function () {
			event.preventDefault();
			var form = event.target;

			var file = form.file.files[0];
			console.log(file);
			// [upload in storage here]
		});
	});
</script>

If you select the file and hit submit, you will see your selected file object in the browser console. Now you need to do 2 things; initialize firebase, and upload the file. To initialize firebase, you need to create an app at Firebase Console.

  1. Add new project.
  2. Enter name of project.
  3. When project is created, select “web”.
  4. Copy the firebaseConfig variable.

Replace the [initialize firebase here] section with the following:

import { initializeApp } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-app.js";

import { getStorage, ref as stRef, uploadBytes } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";

import { getDatabase, ref as dbRef, push, set } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-database.js";

// Your web app's Firebase configuration
const firebaseConfig = {
	apiKey: "",
	authDomain: "",
	projectId: "",
	storageBucket: "",
	messagingSenderId: "",
	appId: ""
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

// Get a reference to the storage service, which is used to create references in your storage bucket
const storage = getStorage(app);

const database = getDatabase();
const databaseReference = dbRef(database, "files");

Replace the firebaseConfig variable with your firebase configurations. Now replace the [upload in storage here] section with the following:

const storageRef = stRef(storage, "files/" + file.name);

uploadBytes(storageRef, file).then(function (snapshot) {
	var newFileRef = push(databaseReference);

	set(newFileRef, {
		"name": file.name
	});
});

This will upload the file in Firebase Storage in a folder named “files”. You can see it in your project’s dashboard in left menu in “Storage” page. Also, it will create a new element in “files” array in “Realtime Database” page.

Displaying Uploaded Files in Firebase Storage

Now the file is being uploaded in Firebase Storage and it’s path is also being saved in realtime database. Now you need to show all uploaded files. Replace the [show all uploaded here] section with the following:

<table>
	<tr>
		<th>ID</th>
		<th>Name</th>
		<th>File</th>
		<th>Actions</th>
	</tr>

	<tr v-for="file in files">
		<td>{{ file.id }}</td>
		<td>{{ file.name }}</td>
		<td>
			[download link here]
		</td>
		<td>
			[delete button here]
		</td>
	</tr>
</table>

This will create a simple HTML table. Now you need to do the following things:

  1. Initialize Vue JS.
  2. Get all data from realtime database.
  3. Render in HTML table.

You can initialize the Vue JS from the following code:

var vueApp = new Vue({
	el: "#app",
	data: {
		files: []
	},
    // [updated event goes here]
});

Then include “onValue” in your “firebase-database” import. So your firebase database import line will become:

import { getDatabase, ref as dbRef, push, set, onValue } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-database.js";

Note the “onValue” in the import block. Similarly, add the “getDownloadURL” in the import of “firebase-storage”. So your firebase storage import line will become:

import { getStorage, ref as stRef, uploadBytes, getDownloadURL } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";

Note the “getDownloadURL” in the import block. Then write the following code in your Javascript:

onValue(databaseReference, function (snapshot) {
	snapshot.forEach(function (childSnapshot) {
		const value = childSnapshot.val();

		const storageRefDownload = stRef(storage, "files/" + value.name);
		getDownloadURL(storageRefDownload).then(function (url) {
			vueApp.files.push({
				"id": childSnapshot.key,
				"name": value.name,
				"url": url
			});
		});
	});
});

You can apply the following CSS styles to make the table look a little nice:

<style>
	table, th, td {
		border: 1px solid black;
		border-collapse: collapse;
	}
	th, td {
		padding: 25px;
	}
</style>

Download File from Firebase Storage

Now you need to download the file. First, you need to replace the [download link here] section with the following:

<a v-bind:href="file.url" v-bind:download="file.name" onclick="downloadFile();">Download</a>

Create the following function in your Javascript:

function downloadFile() {
	// prevent default href action
	event.preventDefault();

	// get URL from href
	var anchor = event.target;
	var url = anchor.getAttribute("href");

	// get blob data
	const xhr = new XMLHttpRequest();
	xhr.responseType = "blob";
	xhr.onload = function (event) {
		const blob = xhr.response;

		// get clickable URL of blob data
		const blogUrl = window.URL.createObjectURL(blob);

		// replace href with new blob value
		anchor.setAttribute("href", blogUrl);

		// remove the onclick listener
		anchor.removeAttribute("onclick");

		// download the file
		anchor.click();

		// free up the memory
		window.URL.revokeObjectURL(blogUrl);
	};
	xhr.open("GET", url);
	xhr.send();
}

// make the function global so it can be accessible from anchor tag onclick
window.downloadFile = downloadFile;

Run the code now, and you will be able to download the file.

Note: If you get a CORS error while working in localhost, then do the following steps:

  1. Download gsutil from here.
  2. Extract the ZIP and paste the folder to a permanent location in your system.
  3. Open the terminal or command prompt in that extracted folder and run the following command in it:
./install.sh
  1. Once installed, run the following command to initialize it:
gcloud init
  1. It will ask for your project name too.
  2. After the project is set and gcloud is initialized, create a file named “cors.json” at the root of your project.
  3. Your “cors.json” must have the following content:
[
	{
		"origin": ["*"],
		"method": ["GET"],
		"maxAgeSeconds": 3600
	}
]
  1. Then run the following command in your terminal:
gsutil cors set cors.json gs://<your-cloud-storage-bucket>
  1. Now try to download the file again, your CORS error should be fixed.

Delete File from Firebase Storage

First, replace the [delete button here] section with the following:

<form class="delete-form">
	<input type="hidden" name="id" v-bind:value="file.id" />
	<input type="hidden" name="name" v-bind:value="file.name" />
	<input type="submit" value="Delete" />
</form>

Then add “deleteObject” in your import of “firebase-storage”. So your import line will look like this:

import { getStorage, ref as stRef, uploadBytes, getDownloadURL, deleteObject } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";

Then add the following event in your [updated event goes here] section:

updated: function () {
	var forms = document.querySelectorAll(".delete-form");
    for (var a = 0; a < forms.length; a++) {
    	forms[a].setAttribute("onsubmit", "onDeleteFormSubmit();");
    }
}

This will attach an event listener to every delete form, which will be called when the form is submitted. Now you need to create the following function in your Javascript:

function onDeleteFormSubmit() {
	event.preventDefault();
	var form = event.target;

	const tempDbRef = dbRef(database, "files/" + form.id.value);
	set(tempDbRef, null);

	const deleteStorageReference = stRef(storage, "files/" + form.name.value);
	deleteObject(deleteStorageReference);

	for (var a = 0; a < vueApp.files.length; a++) {
		if (vueApp.files[a].id == form.id.value) {
			vueApp.files.splice(a, 1);
			break;
		}
	}
}
window.onDeleteFormSubmit = onDeleteFormSubmit;

This will first delete the data from the real-time database. Then it will delete the file from Firebase Storage. Finally, it will remove it from the Vue JS array, so it will automatically be removed from the HTML table too.

You can also create a realtime chat in Firebase. Learn how to do it from here.

Now you will be able to upload files to the Firebase Storage and save its path in Realtime Database. And also to download and delete the files from it. If you face any problems facing this tutorial, kindly let us know in the comments section below.

Events and listeners – Laravel

In this tutorial, we are going to teach you how you can implement events and listeners in Laravel. Events are created and multiple listeners can be assigned to them. Listeners are responsible for performing an action like saving the data in the database, sending an email, etc. In your controllers, you only need to emit an event and all the listeners associated with it will be called automatically.

Video tutorial:

Creating an event

First, we are going to create an event. Open terminal or command prompt at the root of your Laravel project and run the following command:

php artisan make:event MyEvent

A new file will be created at app/Events/MyEvent.php. Now place all the variables in it that you will be passed while calling this event.

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MyEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $my_value;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($my_value)
    {
        $this->my_value = $my_value;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

Creating a Listener

Now we need to create a listener that will be called when the above event is emitted. Open the terminal and run the following command in it:

php artisan make:listener MyListener

A new file will be created at app/Listeners/MyListener.php. It will have the following content:

<?php

namespace App\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class MyListener
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  object  $event
     * @return void
     */
    public function handle($event)
    {
        dd($event->my_value);
    }
}

That $event variable will hold all the values sent from the event class (MyEvent, in this case).

Attach Listeners to Event

The event and listeners are created. Now is the time to tell the Laravel framework to which listeners should be called when a specific event is called. Goto app/Providers/EventServiceProvider.php and include the event and listener at the top:

use App\Events\MyEvent;
use App\Listeners\MyListener;

Then update the $listen array in it:

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    MyEvent::class => [
        MyListener::class
    ]
];

Now, whenever a MyEvent event is emitted, the MyListener listener will automatically be called.

Emitting an Event

Emitting an event in your Laravel project is very easy. You just need to include your event at the top:

use App\Events\MyEvent;

Then whenever you need to call it, you can simply call it like this:

event(new MyEvent("adnan-tech.com"));

At this point, the event will be emitted and the listener will be called. But we need to find a way to check if the listener is actually being called. We can simply do dd() inside listener’s handle() function.

Financial Ledger in Node JS, Mongo DB, and Vue JS

A Financial Ledger script is created in Node JS, Mongo DB, and Vue JS. A financial ledger is used to track your daily finances, for example, your income, your expenses, etc. This script allows you to track your total income, total spending, and your current total balance. They are referred to as “stats” in the script. You can debit the entry by entering the amount that is negative. And you can credit an entry by simply writing its amount.

We have used the HTML and CSS code from this codepen. We have made it dynamic by using the latest technologies like, Vue JS for front-end, Node JS for back-end, and Mongo DB as the database. It has complete CRUD (Create, Read, Update, Delete) operation. It has the following features:

  1. Insert data in Mongo DB using Node JS.
  2. Fetch all documents from Mongo DB.
  3. Update specific document in Mongo DB.
  4. Delete documents from Mongo DB.
  5. Case-insensitive search in Mongo DB documents.
  6. Search by sub-string from Mongo DB document’s keys.
  7. Realtime data insert.
  8. Real-time update.
  9. Realtime deletes.
  10. “Load more” capability.
  11. Datetimepicker library.
  12. EJS templating engine.
  13. Express-formidable for handling form fields.
  14. Skip, limit and sort in Mongo DB.

Video tutorial:

1. Insert Data in Mongo DB using Node JS

An interface that allows you to enter the values (time, description, and amount), created in Vue JS. It binds those input field values to the Vue JS model. To enter the time, we are using a library datetimepicker. We are calling an AJAX request to the Node JS server with the input field values as FormData object when the form submits. That will simply insert a new document in Mongo DB collection “entries”. When the data is inserted, it is also inserted in the Vue JS array. This allows the new data to be appended automatically without having to refresh the page. The stat values automatically are updated based on if the entry is debit or credit.

2. Fetch All Documents from Mongo DB

An AJAX request is sent from the client to view all the data from the database. The server will return all the documents sorting from latest to oldest i.e. newest records will be shown first. The sorting is performed based on the date field selected during insertion. When the data is returned, it is concatenated in the Vue JS array that allows the user to view the data when the page loads. When the data is fetched, the stats are also updated automatically.

3. Update Specific Document in Mongo DB

To update specific documents, we are using Mongo DB auto-generated ObjectId field “_id”. This allows you to search the document, then we can perform the update query. To update the document, we are using HTML’s contenteditable attribute that allows you to edit the HTML node innerHTML by just typing in it. When the content of that div changes, we are sending an AJAX request to the Node JS server that performs the update query. If you update the amount value, then the stats will also get a change in real-time.

4. Delete Documents from Mongo DB

Deleting an entry will remove the document from the Mongo DB collection too. The row will be removed and the stats will be updated too.

5. Case insensitive Search Mongo DB

You can search the entries by description you have put during the insertion, or you can search the entries by either they are debited or credited. For example, you can write “debit” in the search box and it will show all the entries that are debited. The same goes for the “credit” search query.

6. Sub-string Search Mongo DB

While searching by description, you do not need to know the exact words. You can type the part of the description you have remembered and it will search the record anyway.

7. Realtime Mongo DB Data Insert

When a new entry is added, it is automatically prepended in the list using Vue JS. You do not have to refresh the page to view the new entries.

8. Realtime Update in Mongo DB

The same goes for the updation. You can update the entry’s description or the amount by simply typing in its box. When you start typing it automatically gets updated in the Mongo DB too. Also, the stats values also get updated if there is any change in the amount.

9. Realtime Delete in Mongo DB

With each entry, there is a minus (-) sign. On clicking that button, it will remove the entry from the list and also from the Mongo DB. The stats also gets updated accordingly.

10. Load More Capability

In the real-world, when you are creating a financial ledger, there will be hundreds of entries. So loading all entries when the page loads will slow down your application. So what we do is, load a few entries when the page loads. Then we show a button called “Load more”. Upon clicking that button, we will fetch the next entries from Mongo DB and so on. We will be using AJAX to fetch more entries.

11. Datetimepicker Javascript

In the financial ledger, it is important to enter the date the entry was added. So on the web, we have a library called datetimepicker by XDSoft. We are using this library to enter dates and times easily.

12. EJS Templating Engine in Node JS

In Node JS, to render files, there is an engine called EJS. It is used to render HTML files. The rendered files will have an extension “.ejs” instead of “.html” or “.php”.

13. Express Formidable Module in Node JS

In Node JS, to handle FormData object sent using AJAX, we are using a Node JS module named express-formidable. As the complete app is in Javascript, so there are a lot of AJAX requests in this app. Each AJAX request will send a FormData object to send values that the server will process.

14. Skip, Limit and Sort in Mongo DB

While fetching the records from Mongo DB, you can skip the records that are already been displayed to the user. Similarly, to load the data faster, you can limit the number of records to be fetched from Mongo DB in one request. To display the latest entries added, you can sort the records fetched from Mongo DB.

You can download the required assets from here: