#用户头像与个人资料:集成 Gravatar 头像与上传自定义头像
📂 所属阶段:第三阶段 — 用户系统(安全篇)
🔗 相关章节:Flask-Login 实战 · 静态文件管理
#1. Gravatar 头像
#1.1 什么是 Gravatar?
Gravatar(Globally Recognized Avatar)让你用邮箱注册一个头像,全网通用。
#1.2 获取 Gravatar URL
import hashlib
def get_gravatar_url(email, size=80):
"""生成 Gravatar URL"""
# 1. 取邮箱 MD5(不区分大小写)
email_hash = hashlib.md5(email.strip().lower().encode()).hexdigest()
# 2. 拼接 URL
return f"https://www.gravatar.com/avatar/{email_hash}?s={size}&d=identicon"
# 使用
gravatar_url = get_gravatar_url("alice@example.com")
# → https://www.gravatar.com/avatar/5aba7...d1?s=80&d=identicon#1.3 Gravatar 参数
| 参数 | 值 | 说明 |
|---|---|---|
d | identicon | 几何图形(默认) |
d | mp | 简单人像 |
d | retro | 像素风格 |
d | wavatar | 卡通脸 |
d | 404 | 未找到时返回 404 |
s | 80 | 头像尺寸(像素) |
r | g | 评分限制(g/pg/r/x) |
def get_gravatar(email, size=80, rating="g", default="identicon"):
h = hashlib.md5(email.strip().lower().encode()).hexdigest()
return f"https://www.gravatar.com/avatar/{h}?s={size}&d={default}&r={rating}"#1.4 在模板中使用
<!-- 简单使用 -->
<img src="{{ get_gravatar(current_user.email, size=40) }}"
alt="{{ current_user.username }}"
class="rounded-circle">
<!-- 带默认图片 -->
<img src="{{ get_gravatar(user.email, 80) if user.email else '/static/img/default-avatar.png' }}"
alt="{{ user.username }}">#1.5 Flask 中注册 Jinja2 全局函数
# app/extensions.py
import hashlib
def get_gravatar_url(email, size=80, rating="g", default="identicon"):
h = hashlib.md5(email.strip().lower().encode()).hexdigest()
return f"https://www.gravatar.com/avatar/{h}?s={size}&d={default}&r={rating}"
# app/__init__.py
def create_app():
app = Flask(__name__)
# ...
app.jinja_env.globals["get_gravatar"] = get_gravatar_url
return app#2. 自定义头像上传
#2.1 模型扩展
# app/models/user.py
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
username = db.Column(db.String(50))
password_hash = db.Column(db.String(256))
# 头像路径(相对路径)
avatar = db.Column(db.String(200), default="")
bio = db.Column(db.String(500))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def get_avatar(self, size=80):
"""优先使用自定义头像,否则用 Gravatar"""
if self.avatar:
return self.avatar
if self.email:
import hashlib
h = hashlib.md5(self.email.strip().lower().encode()).hexdigest()
return f"https://www.gravatar.com/avatar/{h}?s={size}&d=identicon"
return "/static/img/default-avatar.png"#2.2 上传路由
# app/routes/profile.py
import os
import uuid
from flask import Blueprint, request, redirect, url_for, flash, current_app
from flask_login import login_required, current_user
from werkzeug.utils import secure_filename
from PIL import Image # pip install Pillow
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "webp"}
def allowed_file(filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
@profile_bp.route("/profile/avatar", methods=["POST"])
@login_required
def upload_avatar():
if "avatar" not in request.files:
flash("没有检测到文件!", "danger")
return redirect(url_for("profile.edit_profile"))
file = request.files["avatar"]
if file.filename == "":
flash("请选择一张图片!", "warning")
return redirect(url_for("profile.edit_profile"))
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
ext = filename.rsplit(".", 1)[1].lower()
new_filename = f"{uuid.uuid4().hex}.{ext}"
upload_dir = os.path.join(
current_app.root_path, "static", "uploads", "avatars"
)
os.makedirs(upload_dir, exist_ok=True)
filepath = os.path.join(upload_dir, new_filename)
# 处理图片:缩放到 200x200
img = Image.open(file)
img = img.convert("RGB")
img.thumbnail((200, 200), Image.LANCZOS)
img.save(filepath, "JPEG", quality=85)
# 删除旧头像
if current_user.avatar and current_user.avatar.startswith("/static"):
old_path = os.path.join(
current_app.root_path, current_user.avatar.lstrip("/")
)
if os.path.exists(old_path):
os.remove(old_path)
# 保存新头像路径
current_user.avatar = f"/static/uploads/avatars/{new_filename}"
db.session.commit()
flash("头像已更新!", "success")
else:
flash("不支持的图片格式!", "danger")
return redirect(url_for("profile.edit_profile"))#2.3 头像上传表单
<!-- templates/profile/avatar_form.html -->
<div class="avatar-upload">
<img src="{{ current_user.get_avatar(200) }}"
alt="当前头像" class="avatar-preview rounded-circle">
<form method="POST" enctype="multipart/form-data"
action="{{ url_for('profile.upload_avatar') }}">
<input type="file" name="avatar" accept="image/*" required>
<button type="submit" class="btn btn-primary btn-sm mt-2">上传新头像</button>
</form>
{% if current_user.avatar %}
<a href="{{ url_for('profile.remove_avatar') }}"
class="btn btn-outline-secondary btn-sm mt-2">恢复 Gravatar</a>
{% endif %}
</div>#3. 小结
# 头像方案选择
# 1. Gravatar(零存储,全球通用)
h = hashlib.md5(email.lower().encode()).hexdigest()
avatar_url = f"https://www.gravatar.com/avatar/{h}"
# 2. 自定义上传(用户自由度更高)
# 需要:werkzeug + PIL + 文件存储
# 3. 组合方案(推荐)
# 优先自定义头像,无则用 Gravatar
def get_avatar(user):
return user.avatar or get_gravatar(user.email)💡 最佳实践:Gravatar 是零成本的默认方案,用户体验好。自定义上传则给用户更多选择,两者结合最佳。
🔗 扩展阅读

