# 專案如何連接Firebase
## 步驟1 找到專案設定

## 步驟2 新增網頁應用程式

## 步驟3 註冊名稱 按下註冊應用程式

## 步驟4 新增Firebase SDK

## 步驟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

## 步驟2 點擊開始使用

## 步驟3 以正式版模式啟動

## 步驟4 選雲端位置 (靠亞洲速度比較快)

## 步驟5 讀取後的樣子

## 步驟6 點擊左上方規則,修改規則 (上傳圖片上來的規則)

## 步驟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 修改後

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

```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 送出給後端