Firebase এর হাতেখড়ি (পর্ব ৯) – Firebase Storage: ফাইল ও ইমেজ আপলোড

Firebase Storage ফাইল ও ইমেজ আপলোড

আগের পর্বে Realtime Database-এর Security Rules শিখেছিলাম। কিন্তু RTDB টেক্সট আর সংখ্যা রাখার জন্য ভালো — ছবি, PDF, বা যেকোনো ফাইল সরাসরি ডেটাবেজে রাখা ঠিক না। এই কাজের জন্য আছে Firebase Storage — এই পর্বে শিখব ফাইল আপলোড, ডাউনলোড লিংক তৈরি, আর Storage-এর নিজস্ব Security Rules।

কেন ছবি সরাসরি Database-এ রাখা যায় না?

RTDB-তে ছবি base64 স্ট্রিং হিসেবে গুঁজে দেওয়া টেকনিক্যালি সম্ভব, কিন্তু এতে ডেটাবেজ দ্রুত ফুলে যায়, প্রতিটা read-এ পুরো ছবি লোড হয়ে যায় (even শুধু টাইটেল দরকার হলেও), আর ফ্রি টিয়ারের স্টোরেজ সীমা দ্রুত শেষ হয়ে যায়। Storage আলাদা রাখলে RTDB-তে শুধু ছবির URL রাখা হয় — হালকা, দ্রুত।

Console-এ Storage চালু করা

  1. Firebase Console-এ Build → Storage-এ যান
  2. Get started চাপুন
  3. Security Rules-এর মোড বেছে নিন (শেখার সময় test mode, প্রোডাকশনে অবশ্যই বদলাতে হবে)
  4. একটা 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:

  1. নিজের প্রজেক্টে একটা ফাইল ইনপুট বানাও, uploadBytes দিয়ে একটা ছবি Storage-এ আপলোড করো, তারপর getDownloadURL দিয়ে লিংক বের করে সেই ছবিটা পেজে দেখাও।
  2. ওই URL-টা RTDB-এর কোনো একটা নোডে সেভ করো, যাতে বই আর তার কভার একসাথে জোড়া লাগে।
  3. uploadBytesResumable দিয়ে একটা প্রোগ্রেস বার বানাও।
  4. Storage Rules-এ ৫MB সীমা আর image/.* কনটেন্ট-টাইপ চেক যোগ করে, ৫MB-এর বড় ফাইল বা PDF আপলোড করার চেষ্টা করে দেখো Rules আটকায় কিনা।

পরের পর্বে যা থাকছে

পরের পর্বে আমরা শিখব Firebase CLI, Emulator ও Error Handling — কীভাবে লোকাল মেশিনে Firebase টেস্ট করতে হয় লাইভ ডেটাবেজ না ছুঁয়ে, আর কমন error কোডগুলো কীভাবে সুন্দরভাবে হ্যান্ডেল করতে হয়।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম