What is Firebase Storage?
Firebase Cloud Storage is a powerful, simple, and cost-effective object storage service built for Google scale. It lets you store and serve user-generated content, such as photos, videos, and other files. Firebase Storage is backed by Google Cloud Storage and provides secure uploads and downloads for your Firebase apps.
With Firebase Storage, you can easily upload files from the client directly without needing your own server, and files are stored in a Google Cloud Storage bucket.
π¦ Key Features
- π Secure: Firebase Security Rules protect your files.
- π Scalable: Automatically scales to handle any amount of data.
- π CDN-backed: Files are served from a global CDN for fast delivery.
- π± Offline Support: Automatically handles network interruptions.
- βΈοΈ Resumable: Uploads and downloads can be paused and resumed.
Setting Up Firebase Storage
Initialize Storage in your application:
import { initializeApp } from 'firebase/app';
import { getStorage } from 'firebase/storage';
const firebaseConfig = {
// Your config here
storageBucket: 'your-project.appspot.com'
};
const app = initializeApp(firebaseConfig);
const storage = getStorage(app);
export { storage };
Storage Structure
Files in Storage are organized in a hierarchical structure similar to a file system:
// Storage bucket structure
gs://your-bucket/
βββ users/
β βββ user1/
β β βββ avatar.jpg
β β βββ documents/
β β βββ resume.pdf
β βββ user2/
β βββ avatar.png
βββ posts/
β βββ post1/
β β βββ image1.jpg
β β βββ image2.jpg
β βββ post2/
β βββ cover.jpg
βββ public/
βββ logo.png
Uploading Files
Upload files from various sources:
Upload from File Input
import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';
async function uploadFile(file, path) {
// Create a reference to the file location
const storageRef = ref(storage, path);
// Upload the file
const snapshot = await uploadBytes(storageRef, file);
console.log('Uploaded file:', snapshot.metadata.name);
// Get the download URL
const downloadURL = await getDownloadURL(snapshot.ref);
return downloadURL;
}
// Usage with file input
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
const url = await uploadFile(file, `uploads/${file.name}`);
console.log('File available at:', url);
});
Upload with Progress Tracking
import { ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage';
function uploadFileWithProgress(file, path, onProgress) {
const storageRef = ref(storage, path);
const uploadTask = uploadBytesResumable(storageRef, file);
return new Promise((resolve, reject) => {
uploadTask.on('state_changed',
(snapshot) => {
// Track progress
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
onProgress(progress);
switch (snapshot.state) {
case 'paused':
console.log('Upload paused');
break;
case 'running':
console.log('Upload running');
break;
}
},
(error) => {
// Handle errors
reject(error);
},
async () => {
// Upload completed
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
resolve(downloadURL);
}
);
});
}
// Usage
const url = await uploadFileWithProgress(file, 'images/photo.jpg', (progress) => {
console.log(`Upload is ${progress}% done`);
});
Upload from String (Base64, Data URL)
import { ref, uploadString, getDownloadURL } from 'firebase/storage';
// Upload base64 encoded string
async function uploadBase64(base64String, path) {
const storageRef = ref(storage, path);
const snapshot = await uploadString(storageRef, base64String, 'base64');
return getDownloadURL(snapshot.ref);
}
// Upload data URL (e.g., from canvas)
async function uploadDataURL(dataURL, path) {
const storageRef = ref(storage, path);
const snapshot = await uploadString(storageRef, dataURL, 'data_url');
return getDownloadURL(snapshot.ref);
}
// Upload from canvas
const canvas = document.getElementById('myCanvas');
const dataURL = canvas.toDataURL('image/png');
const url = await uploadDataURL(dataURL, 'images/drawing.png');
Adding Metadata
Attach custom metadata to files:
import { ref, uploadBytes, updateMetadata } from 'firebase/storage';
// Upload with metadata
async function uploadWithMetadata(file, path, customMetadata) {
const storageRef = ref(storage, path);
const metadata = {
contentType: file.type,
customMetadata: {
uploadedBy: customMetadata.userId,
description: customMetadata.description,
originalName: file.name
}
};
const snapshot = await uploadBytes(storageRef, file, metadata);
return snapshot;
}
// Update metadata after upload
async function updateFileMetadata(path, newMetadata) {
const storageRef = ref(storage, path);
const updatedMetadata = await updateMetadata(storageRef, {
customMetadata: newMetadata
});
return updatedMetadata;
}
Downloading Files
Get download URLs and fetch files:
import { ref, getDownloadURL, getBlob, getBytes } from 'firebase/storage';
// Get download URL
async function getFileURL(path) {
const storageRef = ref(storage, path);
const url = await getDownloadURL(storageRef);
return url;
}
// Download as Blob (for files up to memory limit)
async function downloadAsBlob(path) {
const storageRef = ref(storage, path);
const blob = await getBlob(storageRef);
return blob;
}
// Download as bytes
async function downloadAsBytes(path) {
const storageRef = ref(storage, path);
const bytes = await getBytes(storageRef);
return bytes;
}
// Usage: Display image
const url = await getFileURL('images/photo.jpg');
document.getElementById('image').src = url;
Deleting Files
Remove files from storage:
import { ref, deleteObject } from 'firebase/storage';
async function deleteFile(path) {
const storageRef = ref(storage, path);
try {
await deleteObject(storageRef);
console.log('File deleted successfully');
} catch (error) {
if (error.code === 'storage/object-not-found') {
console.log('File does not exist');
} else {
throw error;
}
}
}
// Delete user's avatar
await deleteFile('users/user123/avatar.jpg');
Listing Files
List files in a directory:
import { ref, listAll, list } from 'firebase/storage';
// List all files in a folder
async function listAllFiles(path) {
const storageRef = ref(storage, path);
const result = await listAll(storageRef);
// Process items (files)
const files = await Promise.all(
result.items.map(async (item) => ({
name: item.name,
fullPath: item.fullPath,
url: await getDownloadURL(item)
}))
);
// Process prefixes (folders)
const folders = result.prefixes.map((folder) => ({
name: folder.name,
fullPath: folder.fullPath
}));
return { files, folders };
}
// Paginated listing (for large directories)
async function listFilesPaginated(path, maxResults = 10, pageToken = null) {
const storageRef = ref(storage, path);
const options = { maxResults };
if (pageToken) {
options.pageToken = pageToken;
}
const result = await list(storageRef, options);
return {
files: result.items,
folders: result.prefixes,
nextPageToken: result.nextPageToken // Use for next page
};
}
React File Upload Component
Create a reusable file upload component:
import { useState } from 'react';
import { ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage';
import { storage } from '../lib/firebase';
export function FileUpload({ onUploadComplete, path = 'uploads' }) {
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const [error, setError] = useState(null);
const handleFileChange = async (e) => {
const file = e.target.files[0];
if (!file) return;
setUploading(true);
setError(null);
const fileName = `${Date.now()}-${file.name}`;
const storageRef = ref(storage, `${path}/${fileName}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
setProgress(progress);
},
(error) => {
setError(error.message);
setUploading(false);
},
async () => {
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
setUploading(false);
setProgress(0);
onUploadComplete({ url: downloadURL, name: fileName });
}
);
};
return (
<div>
<input type="file" onChange={handleFileChange} disabled={uploading} />
{uploading && (
<div>
<progress value={progress} max="100" />
<span>{Math.round(progress)}%</span>
</div>
)}
{error && <p className="text-red-500">{error}</p>}
</div>
);
}
// Usage
function ProfilePage() {
const handleUpload = ({ url, name }) => {
console.log('Uploaded:', url);
// Save URL to user profile in database
};
return <FileUpload onUploadComplete={handleUpload} path="avatars" />;
}
Image Resize Before Upload
Resize images client-side before uploading:
async function resizeImage(file, maxWidth, maxHeight) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
let width = img.width;
let height = img.height;
// Calculate new dimensions
if (width > height) {
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
} else {
if (height > maxHeight) {
width *= maxHeight / height;
height = maxHeight;
}
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(resolve, 'image/jpeg', 0.8);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}
// Usage
async function uploadResizedImage(file, path) {
const resizedBlob = await resizeImage(file, 800, 600);
const storageRef = ref(storage, path);
const snapshot = await uploadBytes(storageRef, resizedBlob);
return getDownloadURL(snapshot.ref);
}
π‘ Key Takeaways
- β’ Firebase Storage stores files in Google Cloud Storage buckets
- β’ Use uploadBytes for simple uploads, uploadBytesResumable for progress tracking
- β’ Get download URLs with getDownloadURL to serve files
- β’ Add custom metadata to organize and search files
- β’ Resize images client-side to save bandwidth and storage
- β’ Use Security Rules to protect file access
π Learn More
-
Cloud Storage Documentation β
Official Firebase Storage guides and APIs
-
Upload Files Guide β
Detailed guide on uploading files to Storage
-
Storage Security Rules β
Secure your files with storage rules