今天抽空研究了一下 Google Firebase,顺手做了一个 Python (Streamlit) 的小 Demo,感觉非常强大。写这篇博客记录一下搭建过程,顺便聊聊其中几个有意思的技术点。
1. 什么是 Firebase?它和 SQL、Storage 有啥区别?
很多人可能会问:既然已经有了 Cloud SQL (数据库) 和 Cloud Storage (对象存储),Google 为什么要搞一个 Firebase?
其实它们定位完全不同:
- Cloud SQL (MySQL/PostgreSQL):是传统的关系型数据库,适合存结构化严谨的数据,但通常需要你自己写后端 API 来读写。
- Cloud Storage (Bucket):就是个云盘,用来存文件(图片、视频、文本),不适合存细粒度的业务数据。
- Firebase (Firestore) :它是一个 BaaS (Backend-as-a-Service) 平台。
- 特点:它既是数据库,又自带后端逻辑。
- 核心优势 :支持 Real-time (实时同步)。你在数据库里改个字,客户端不需要刷新,毫秒级自动更新。
2. 实战 Demo:搭建 Python 实时留言板
今天我们就用 Python + Streamlit + Firebase 快速撸一个小项目。
第一步:进入 Firebase 控制台
点击进入标准控制台:👉 https://console.firebase.google.com/
找到并进入你的项目。
第二步:创建数据库 (Firestore)
- 在左侧菜单栏,点击 构建 (Build) -> Firestore Database。
- 点击 创建数据库 (Create Database)。
- 位置 :保持默认(通常是
us-central1)。 - 安全规则 (重点) :一定要选 "以测试模式开始" (Start in test mode) 。
- 注意:这一步很重要,否则你的程序会因为没有权限而报错。
- 点击 Create,等待几秒即可。
第三步:获取"通行证" (key.json)
这是很多人容易卡住的一步,Cloud Console 里比较乱,建议在 Firebase Console 操作:
- 点击左上角 项目概览 (Project Overview) 旁边的 齿轮图标 ⚙️ -> 项目设置。
- 点击顶部的 服务账号 (Service accounts) 选项卡。
- 点击下方的 生成新的私钥 (Generate new private key) 按钮。
- 下载下来的
.json文件,重命名为key.json,并放到你的项目文件夹里。
3. 上代码
确保你的环境已经安装了必要的库:
bash
pip install streamlit firebase-admin google-cloud-storage
新建一个 app.py,代码如下(包含数据库读写 + 读取 Storage 文件):
python
import streamlit as st
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
from firebase_admin import storage # 引入存储模块
import datetime
# ==========================================
# 0. 配置区域 (请修改这里!)
# ==========================================
# 你的 Bucket 名字 (不需要带 gs://)
TARGET_BUCKET_NAME = "your-bucket-name"
# 你想读取的文件名 (比如 123.txt)
TARGET_FILE_NAME = "your.txt"
# ==========================================
# 1. 初始化 Firebase 连接
# ==========================================
# 检查是否已经初始化,防止 Streamlit 刷新页面报错
if not firebase_admin._apps:
# 加载你的管理员密钥
cred = credentials.Certificate("key.json")
# 初始化 App
firebase_admin.initialize_app(cred)
# 获取数据库客户端
db = firestore.client()
# ==========================================
# 2. 页面布局
# ==========================================
st.title("🔥 Firebase 全能演示")
st.caption("集成 Firestore 数据库 + Cloud Storage 文件读取")
# --- 分隔线 ---
st.divider()
# ==========================================
# 3. 功能 A:读取 Bucket 里的 TXT 文件
# ==========================================
st.header("📂 读取存储桶文件")
st.write(f"目标 Bucket: `{TARGET_BUCKET_NAME}` | 目标文件: `{TARGET_FILE_NAME}`")
if st.button("读取远程文件内容"):
try:
# 1. 获取 Bucket 对象
# 注意:这里直接指定 bucket 名字,哪怕它不在 Firebase 项目里也能连(只要有权限)
bucket = storage.bucket(TARGET_BUCKET_NAME)
# 2. 获取文件对象 (Blob)
blob = bucket.blob(TARGET_FILE_NAME)
# 3. 下载并解码 (bytes -> string)
# 如果文件很大,尽量不要用 download_as_string,这里仅做演示
content = blob.download_as_string().decode("utf-8")
# 4. 显示内容
st.success("读取成功!")
st.code(content, language="text") # 用代码框显示,好看一点
except Exception as e:
st.error("读取失败!请检查控制台错误信息。")
st.warning(f"错误详情: {e}")
st.info("提示:如果 Bucket 不是公开的,请确认 key.json 对应的服务账号在这个 Bucket 里有 'Storage Object Viewer' 权限。")
# --- 分隔线 ---
st.divider()
# ==========================================
# 4. 功能 B:留言板 (Firestore)
# ==========================================
st.header("💬 实时留言板")
# 侧边栏:写数据
with st.sidebar:
st.header("📝 发送新消息")
user_name = st.text_input("你的昵称", "Python大佬")
msg_content = st.text_area("留言内容")
if st.button("发送到数据库"):
if msg_content:
try:
# 写入 Firestore
db.collection("messages").add({
"content": msg_content,
"user": user_name,
"timestamp": datetime.datetime.now()
})
st.success("发送成功!")
except Exception as e:
st.error(f"写入失败: {e}")
else:
st.warning("写点东西再发呗")
# 主区域:读数据
if st.button("🔄 刷新留言列表"):
st.rerun()
# 从 Firestore 读取数据 (按时间倒序)
try:
docs = db.collection("messages").order_by("timestamp", direction=firestore.Query.DESCENDING).stream()
msg_count = 0
for doc in docs:
msg_count += 1
data = doc.to_dict()
with st.chat_message("user"):
st.write(f"**{data.get('user', '匿名')}** ({data.get('timestamp', '')})")
st.markdown(data.get('content'))
if msg_count == 0:
st.write("还没有留言,快去侧边栏发一条!")
except Exception as e:
st.error(f"连接数据库失败: {e}")
运行项目:
streamlit run app.py
效果:你可以打开两个浏览器窗口,在一个窗口输入留言,另一个窗口会实时同步出现内容,非常丝滑。
4. 进阶思考:关于 CORS 和 权限
在上面的代码中,我增加了一个功能:读取 Cloud Storage Bucket 中的 .txt 文件。这里引出了两个非常有意思的问题。
问:为什么没有触发 CORS 跨域报错?
如果我们在前端 HTML/JS 里直接 fetch 一个 Bucket 的文件,通常会报 CORS 错误。但在 Python 代码里却没事,为什么?
答 :CORS (Cross-Origin Resource Sharing) 是浏览器的防御机制,用来防止恶意 JS 脚本跨域请求。
- 本 Demo 的原理:请求是由 Python 后端(服务器端)发起的。
- 服务器对服务器 (Server-to-Server) 的通信是不受 CORS 限制的。Python 相当于是一个代理,绕过了浏览器的限制。
问:如果 Bucket 是私有的 (Private),该怎么访问?
如果 Bucket 不是公开的,直接读取会报 403 Forbidden。解决方法是利用 IAM 权限管理。
我们初始化的 key.json 对应一个 Service Account (服务账号)。我们需要把这个"机器人"拉进 Bucket 的白名单里:
- 打开
key.json,找到client_email字段,复制那个xxx@xxx.iam.gserviceaccount.com的邮箱。 - 前往 Google Cloud Console (存储控制台) :https://console.cloud.google.com/storage/browser
- 找到你的目标 Bucket,点击 权限 (Permissions)。
- 点击 授予访问权限 (Grant Access)。
- 新的主帐号:粘贴刚才的邮箱。
- 角色 :搜索并选择 Storage Object Viewer (存储对象查看者)。
- 保存即可。
这样,你的 Python 脚本就拥有了合法的"查看权限",可以愉快地读取私有文件了!
有任何问题,欢迎随时私信交流 ~ 🚀