# 專案如何連接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 送出給後端