Firebase Storage Upload Download

Uploading and downloading files in Firebase Storage requires two things: a storage reference pointing to the file's path, and the file data itself. Firebase provides progress tracking during uploads and secure download URLs for sharing files after upload.

Uploading a File from a Browser Input

import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { storage } from "./firebase";

async function uploadFile(file, userId) {
  // Build the file path using the user's ID
  const filePath = "profile-photos/" + userId + "/" + file.name;
  const storageRef = ref(storage, filePath);

  // Start the upload
  const uploadTask = uploadBytesResumable(storageRef, file);

  return new Promise((resolve, reject) => {
    uploadTask.on(
      "state_changed",
      (snapshot) => {
        // Track progress
        const percent = Math.round(
          (snapshot.bytesTransferred / snapshot.totalBytes) * 100
        );
        console.log("Upload progress:", percent + "%");
      },
      (error) => {
        // Handle errors
        console.error("Upload failed:", error.message);
        reject(error);
      },
      async () => {
        // Upload complete — get the download URL
        const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
        console.log("File available at:", downloadURL);
        resolve(downloadURL);
      }
    );
  });
}

Connecting the Upload to an HTML Input

<input type="file" id="file-input" accept="image/*" />
<button id="upload-btn">Upload Photo</button>
<p id="upload-status"></p>
<progress id="upload-bar" value="0" max="100"></progress>
document.getElementById("upload-btn").addEventListener("click", async () => {
  const fileInput = document.getElementById("file-input");
  const file = fileInput.files[0];
  if (!file) {
    alert("Please select a file first.");
    return;
  }

  const userId = auth.currentUser.uid;
  const filePath = "profile-photos/" + userId + "/" + file.name;
  const storageRef = ref(storage, filePath);
  const uploadTask = uploadBytesResumable(storageRef, file);

  uploadTask.on("state_changed",
    (snapshot) => {
      const percent = Math.round(
        (snapshot.bytesTransferred / snapshot.totalBytes) * 100
      );
      document.getElementById("upload-bar").value = percent;
      document.getElementById("upload-status").textContent = percent + "% uploaded";
    },
    (error) => {
      document.getElementById("upload-status").textContent = "Error: " + error.message;
    },
    async () => {
      const url = await getDownloadURL(uploadTask.snapshot.ref);
      document.getElementById("upload-status").textContent = "Upload complete!";
      console.log("Download URL:", url);
    }
  );
});

Simple Upload Without Progress Tracking

For small files where progress tracking is unnecessary, use uploadBytes:

import { ref, uploadBytes, getDownloadURL } from "firebase/storage";

async function uploadSmallFile(file) {
  const storageRef = ref(storage, "documents/" + file.name);
  const snapshot = await uploadBytes(storageRef, file);
  const url = await getDownloadURL(snapshot.ref);
  return url;
}

Getting the Download URL

The download URL is a public HTTPS link that anyone with the link can use to access the file — as long as your security rules permit it. Store this URL in Firestore alongside the document that references the file:

import { doc, updateDoc } from "firebase/firestore";
import { db } from "./firebase";

// After upload completes, save URL to Firestore
const downloadURL = await getDownloadURL(snapshot.ref);
await updateDoc(doc(db, "users", userId), {
  photoURL: downloadURL
});

Getting a Download URL for an Existing File

import { ref, getDownloadURL } from "firebase/storage";

const fileRef = ref(storage, "profile-photos/uid_alice/photo.jpg");
const url = await getDownloadURL(fileRef);

// Display in an img tag
document.getElementById("profile-img").src = url;

Uploading from a Data URL or Base64 String

import { ref, uploadString } from "firebase/storage";

// Upload a base64-encoded image
const base64Image = "data:image/png;base64,iVBOR...";
const storageRef = ref(storage, "thumbnails/thumb_001.png");

await uploadString(storageRef, base64Image, "data_url");
const url = await getDownloadURL(storageRef);
console.log("Thumbnail URL:", url);

Deleting a File

import { ref, deleteObject } from "firebase/storage";

const fileRef = ref(storage, "profile-photos/uid_alice/old-photo.jpg");
await deleteObject(fileRef);
console.log("File deleted.");

Upload State Types

During an upload, the state_changed listener reports upload states:

  • running — upload is actively transferring data
  • paused — upload was paused by calling uploadTask.pause()
  • canceled — upload was stopped by calling uploadTask.cancel()

You can pause and resume uploads, which is useful for large files on unstable connections:

// Pause
uploadTask.pause();

// Resume
uploadTask.resume();

// Cancel
uploadTask.cancel();

Key Takeaway

Use uploadBytesResumable for large files where progress tracking matters and uploadBytes for small files. Always call getDownloadURL after upload and store the returned URL in Firestore for later use. Use deleteObject to remove files when users delete their accounts or content. Pair file paths with the user's UID to enforce per-user access in security rules.

Leave a Comment

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