---
# System prepended metadata

title: 'Firebase '

---

# 專案如何連接Firebase

## 步驟1 找到專案設定
![image](https://hackmd.io/_uploads/H1iytyHhT.png)

## 步驟2 新增網頁應用程式
![image](https://hackmd.io/_uploads/r1Z7YkB3T.png)

## 步驟3 註冊名稱 按下註冊應用程式
![image](https://hackmd.io/_uploads/H1outyB3p.png)

## 步驟4 新增Firebase SDK
![image](https://hackmd.io/_uploads/BJFjKyH2T.png)

## 步驟5 在專案中下 npm install firebase

## 步驟6 創一個檔案名 例如firebase.js 再貼上它給的code

```javascript
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "AIzaSyACLE4n8Ly8HKFLaokZRy6lhgfEFy3FBRQ",
  authDomain: "vue-petzone.firebaseapp.com",
  projectId: "vue-petzone",
  storageBucket: "vue-petzone.appspot.com",
  messagingSenderId: "220206822424",
  appId: "1:220206822424:web:82136aa67782b3bc6e3f6d"
};

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

## 步驟7 把Api key用環境變數 並且 export app 即完成專案與App連接
```javascript!
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: "vue-petzone.firebaseapp.com",
  projectId: "vue-petzone",
  storageBucket: "vue-petzone.appspot.com",
  messagingSenderId: "220206822424",
  appId: "1:220206822424:web:82136aa67782b3bc6e3f6d"
};

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

```



---



# Firebase storage (存圖)

## 步驟1 firebase左側點Storage
![image](https://hackmd.io/_uploads/ryHpm372a.png)

## 步驟2 點擊開始使用
![image](https://hackmd.io/_uploads/SyUOB1Hhp.png)

## 步驟3 以正式版模式啟動
![image](https://hackmd.io/_uploads/HkSZV373a.png)

## 步驟4 選雲端位置 (靠亞洲速度比較快)
![image](https://hackmd.io/_uploads/rJOfUyHhp.png)

## 步驟5 讀取後的樣子
![image](https://hackmd.io/_uploads/rJhu8yB26.png)


## 步驟6 點擊左上方規則，修改規則 (上傳圖片上來的規則)
![image](https://hackmd.io/_uploads/B1En8JrhT.png)

## 步驟7 修改的部分在有寫中文註解的地方

```javascript
rules_version = '2';

// Craft rules based on data in your Firestore database
// allow write: if firestore.get(
//    /databases/(default)/documents/users/$(request.auth.uid)).data.isAdmin;
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read;   // 可以讀
      allow write: if  // 可以寫 if 資源小於 5 * 1024 * 1024 = 5MB 且 只要是圖檔都可以上傳
      request.resource.size < 5 * 1024 * 1024 && 
      request.resource.contentType.matches('image/.*')
    }
  }
}
```
## 步驟8 修改後
![image](https://hackmd.io/_uploads/SyNk_yHh6.png)




---


# 圖片上傳範例程式 (React) 不過用到的firebase function 應該都一樣

![2024-02-2223-55-17-ezgif.com-video-to-gif-converter](https://hackmd.io/_uploads/BkEa0Jr2p.gif)

```javascript!
import { useEffect, useRef, useState } from "react";
import { useSelector } from "react-redux";
import {
  getDownloadURL,
  getStorage,
  ref,
  uploadBytesResumable,
} from "firebase/storage";
import { app } from "../Firebase";

const Profile = () => {
  // 這是從redux中 取出資料 應該類似於pinia
  const { currentUser } = useSelector((state) => state.user);
 
  // 上傳圖檔的input hidden 在<img />上onclick用
  const fileRef = useRef(null);
  
  // input 接圖檔
  const [image, setImage] = useState(undefined);

  // 上傳百分比
  const [imagePercent, setImagePercent] = useState(0);

  // is firebase error 
  const [imageError, setImageError] = useState(false);

  // 儲存formData 
  const [formData, setFormData] = useState({});

 
  // 當input收到圖檔執行上傳function
  useEffect(() => {
    if (image) {
      handleFileUpload(image);
    }
  }, [image]);

  const handleFileUpload = async (image) => {
    const storage = getStorage(app);
    const fileName = new Date().getTime() + image.name;
    const storageRef = ref(storage, fileName);
    const uploadTask = uploadBytesResumable(storageRef, image);
    uploadTask.on(
      "state_changed",
      (snapshot) => {
        const progress =
          (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        setImagePercent(Math.round(progress));
      },
      (error) => {
        setImageError(true);
      },
        // 取得url後 更新FormData
      () => {
        getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) =>
          setFormData({ ...formData, profilePicture: downloadURL })
        );
      }
    );
  };

  return (
    <div className="p-3 max-w-lg mx-auto">
      <h1 className="text-3xl font-semibold text-center my-7">Profile</h1>
      <form className="flex flex-col gap-4">
        <input
          type="file"
          ref={fileRef}
          hidden
          accept="image/*"
          onChange={(e) => setImage(e.target.files[0])}
        />
        {/*當FormData中有了圖片網址就顯示 */}
        <img
          src={formData.profilePicture || currentUser.profilePicture}
          alt="profile"
          className="h-24 w-24 self-center cursor-pointer rounded-full object-cover mt-2"
          onClick={() => fileRef.current.click()}
        />
        {/* if Error in image upload */}
        <p className="text-sm self-center">
          {imageError ? (
            <span className="text-red-700">
              Error uploading image (file size must be less than 2 MB)
            </span>
          ) : imagePercent > 0 && imagePercent < 100 ? (
            <span className="text-slate-700">{`Uploading: ${imagePercent} %`}</span>
          ) : imagePercent === 100 ? (
            <span className="text-green-700">Image uploaded successfully</span>
          ) : (
            ""
          )}
        </p>
        <input
          defaultValue={currentUser.username}
          type="text"
          id="username"
          placeholder="Username"
          className="bg-slate-100 rounded-lg p-3"
        />
        <input
          defaultValue={currentUser.email}
          type="email"
          id="email"
          placeholder="Email"
          className="bg-slate-100 rounded-lg p-3"
        />
        <input
          type="password"
          id="password"
          placeholder="Password"
          className="bg-slate-100 rounded-lg p-3"
        />
        <button className="bg-slate-700 text-white p-3 rounded-lg uppercase hover:opacity-95 disabled:opacity-80">
          update
        </button>
      </form>
      <div className="flex justify-between mt-5">
        <span className="text-red-700 cursor-pointer">Delete Account</span>
        <span className="text-red-700 cursor-pointer">Sign out</span>
      </div>
    </div>
  );
};

export default Profile;

```

## 最後FormaData都準備好後，弄個Button submit 送出給後端