আগের পর্বে Realtime Database-এর Security Rules শিখেছিলাম। কিন্তু RTDB টেক্সট আর সংখ্যা রাখার জন্য ভালো — ছবি, PDF, বা যেকোনো ফাইল সরাসরি ডেটাবেজে রাখা ঠিক না। এই কাজের জন্য আছে Firebase Storage — এই পর্বে শিখব ফাইল আপলোড, ডাউনলোড লিংক তৈরি, আর Storage-এর নিজস্ব Security Rules।
কেন ছবি সরাসরি Database-এ রাখা যায় না?
RTDB-তে ছবি base64 স্ট্রিং হিসেবে গুঁজে দেওয়া টেকনিক্যালি সম্ভব, কিন্তু এতে ডেটাবেজ দ্রুত ফুলে যায়, প্রতিটা read-এ পুরো ছবি লোড হয়ে যায় (even শুধু টাইটেল দরকার হলেও), আর ফ্রি টিয়ারের স্টোরেজ সীমা দ্রুত শেষ হয়ে যায়। Storage আলাদা রাখলে RTDB-তে শুধু ছবির URL রাখা হয় — হালকা, দ্রুত।
Console-এ Storage চালু করা
- Firebase Console-এ Build → Storage-এ যান
- Get started চাপুন
- Security Rules-এর মোড বেছে নিন (শেখার সময় test mode, প্রোডাকশনে অবশ্যই বদলাতে হবে)
- একটা location বেছে নিন — RTDB-এর মতোই কাছাকাছি একটা region ভালো
SDK সেটআপ
// firebase.js
import { initializeApp } from "firebase/app";
import { getStorage } from "firebase/storage";
const app = initializeApp(firebaseConfig);
export const storage = getStorage(app);
ফাইল আপলোড করা
Storage-এও RTDB-এর মতো ref() ব্যবহার হয় — শুধু এবার path একটা ফাইলের অবস্থান নির্দেশ করে, JSON নোড না।
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { storage } from "./firebase";
async function uploadBookCover(file, bookId) {
const fileRef = ref(storage, `covers/${bookId}.jpg`);
await uploadBytes(fileRef, file);
const downloadURL = await getDownloadURL(fileRef);
console.log("ছবির URL:", downloadURL);
return downloadURL; // এই URL-টাই RTDB-তে সেভ হবে
}
একটা সম্পূর্ণ ফ্লো — HTML ফাইল ইনপুট থেকে ছবি নিয়ে Storage-এ আপলোড করে, সেই URL RTDB-তে বইয়ের সাথে সেভ করা।
import { ref as dbRef, update } from "firebase/database";
import { ref as storageRef, uploadBytes, getDownloadURL } from "firebase/storage";
import { db, storage } from "./firebase";
async function addBookWithCover(bookId, title, imageFile) {
// ১. ছবি Storage-এ আপলোড
const fileRef = storageRef(storage, `covers/${bookId}.jpg`);
await uploadBytes(fileRef, imageFile);
const coverUrl = await getDownloadURL(fileRef);
// ২. RTDB-তে শুধু URL সেভ
await update(dbRef(db, `books/${bookId}`), {
title,
coverUrl,
});
}
লক্ষ্য করুন — ref নামটা RTDB আর Storage দুই জায়গাতেই আছে, তাই import করার সময় as dbRef আর as storageRef দিয়ে আলাদা করা হয়েছে (Advanced JS-এর import renaming প্যাটার্ন)।
আপলোড প্রোগ্রেস দেখানো
বড় ফাইলের জন্য uploadBytes-এর বদলে uploadBytesResumable ব্যবহার করলে আপলোডের শতকরা অগ্রগতি ট্র্যাক করা যায় — একটা প্রোগ্রেস বার দেখাতে এটা জরুরি।
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { storage } from "./firebase";
function uploadWithProgress(file, path, onProgress) {
return new Promise((resolve, reject) => {
const fileRef = ref(storage, path);
const task = uploadBytesResumable(fileRef, file);
task.on(
"state_changed",
(snapshot) => {
const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
onProgress(Math.round(percent));
},
(error) => reject(error),
async () => {
const url = await getDownloadURL(task.snapshot.ref);
resolve(url);
}
);
});
}
ফাইল ডিলিট করা
import { ref, deleteObject } from "firebase/storage";
import { storage } from "./firebase";
async function deleteBookCover(bookId) {
const fileRef = ref(storage, `covers/${bookId}.jpg`);
await deleteObject(fileRef);
console.log("ছবি মুছে ফেলা হয়েছে");
}
Storage Security Rules
Storage-এর নিজস্ব Rules সিনট্যাক্স RTDB থেকে একটু আলাদা — এখানে ফাইল সাইজ ও টাইপও নিয়ন্ত্রণ করা যায়, যা RTDB Rules-এ সম্ভব না।
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /covers/{fileName} {
allow read: if true; // যে কেউ ছবি দেখতে পারবে
allow write: if request.auth != null
&& request.resource.size < 5 * 1024 * 1024 // ৫MB সীমা
&& request.resource.contentType.matches('image/.*'); // শুধু ছবি
}
}
}
এখানে তিনটা শর্ত একসাথে চেক হচ্ছে — লগইন থাকতে হবে, ফাইল ৫MB-এর কম হতে হবে, আর কনটেন্ট-টাইপ অবশ্যই ছবি হতে হবে (PDF বা .exe ফাইল আপলোড ঠেকাতে)।
ইন্টারেক্টিভ — আপলোড প্রোগ্রেস সিমুলেশন
নিচে uploadBytesResumable-এর progress ইভেন্ট কীভাবে কাজ করে তা সিমুলেট করা হয়েছে।
ভালো অভ্যাস
- ফাইলের নামে ইউনিক আইডি ব্যবহার করুন — যেমন
${bookId}.jpg, নাহলে একই নামের ফাইল একে অপরকে ওভাররাইট করে দেবে - ফাইল সাইজ ও টাইপ সবসময় Rules-এ যাচাই করুন — শুধু ফ্রন্টএন্ডে চেক করলে বাইপাস করা যায়
- বড় ফাইলে সবসময় uploadBytesResumable ব্যবহার করুন — ইউজারকে প্রোগ্রেস দেখানো ভালো UX
নিজে চেষ্টা করো
Exercise:
- নিজের প্রজেক্টে একটা ফাইল ইনপুট বানাও,
uploadBytesদিয়ে একটা ছবি Storage-এ আপলোড করো, তারপরgetDownloadURLদিয়ে লিংক বের করে সেই ছবিটা পেজে দেখাও। - ওই URL-টা RTDB-এর কোনো একটা নোডে সেভ করো, যাতে বই আর তার কভার একসাথে জোড়া লাগে।
uploadBytesResumableদিয়ে একটা প্রোগ্রেস বার বানাও।- Storage Rules-এ ৫MB সীমা আর
image/.*কনটেন্ট-টাইপ চেক যোগ করে, ৫MB-এর বড় ফাইল বা PDF আপলোড করার চেষ্টা করে দেখো Rules আটকায় কিনা।
পরের পর্বে যা থাকছে
পরের পর্বে আমরা শিখব Firebase CLI, Emulator ও Error Handling — কীভাবে লোকাল মেশিনে Firebase টেস্ট করতে হয় লাইভ ডেটাবেজ না ছুঁয়ে, আর কমন error কোডগুলো কীভাবে সুন্দরভাবে হ্যান্ডেল করতে হয়।