# 基于 Flask 的个人博客系统设计文档 ## 1. 概述 本系统是一个轻量级的个人博客内容管理方案,支持通过 Web 界面上传 Markdown 博文及关联图片。后端基于 Flask 完成 Markdown 到 HTML 的渲染(含 LaTeX 支持),生成静态页面并保存。不需要数据库,所有数据以文件系统方式组织。前端采用瀑布流布局展示文章列表,风格简洁美观,仿照朋友圈的卡片式设计。 ## 2. 技术架构 - **后端框架**:Flask - **Markdown 渲染**:`markdown` + 扩展(codehilite, fenced_code, tables 等) - **LaTeX 渲染**:前端 MathJax 3(服务端保留 `$...$` / `$$...$$` 标记) - **文件存储**:按文章 ID 分目录存储原始 Markdown、图片,另生成独立静态 HTML 文件 - **元数据管理**:单一 `posts_index.json` 文件存储所有文章索引(无数据库) - **前端布局**:CSS Grid 实现瀑布流卡片,深色/浅色干净主题,响应式设计 ## 3. 目录结构 ``` . ├── app.py # Flask 主程序,路由与核心逻辑 ├── config.py # 配置文件(上传限制、路径等) ├── requirements.txt # Python 依赖列表 ├── templates/ # Jinja2 模板 │ ├── base.html # 全局基础模板(含 meta、MathJax、导航栏) │ ├── index.html # 博客首页(瀑布流文章列表) │ ├── upload.html # 上传表单页面 │ └── admin.html # 管理页面(展示所有文章,提供删除操作) ├── static/ # 静态资源(Flask 自动提供) │ ├── css/ │ │ └── style.css # 主样式表(瀑布流、卡片、暗色/亮色风格) │ ├── js/ │ │ └── main.js # 可选前端交互(如瀑布流优化、懒加载) │ └── uploads/ # 用户上传的图片存储目录 │ └── posts/ # 每篇文章的图片子目录(详见下文) ├── generated/ # 生成的静态 HTML 文件 │ └── .html # 每篇文章对应的独立页面 ├── posts_data/ # 原始数据存储 │ └── / # 以文章 ID 命名的目录 │ ├── content.md # 原始 Markdown 文件 │ └── images/ # 该文章用到的图片(软链接或实际存储) │ └── 图片文件... └── posts_index.json # 全局索引文件,存储所有文章元数据 ``` > **说明**: > - 图片实际保存到 `static/uploads/posts//` 下,以便通过 `/static/uploads/posts//image.png` 直接访问。 > - `posts_data//images/` 可以是一个目录软链接,也可以在主程序中统一按 ID 查找图片路径。为简化,可以在上传时直接将图片保存到 `static/uploads/posts//`,而 `posts_data` 中只保留 Markdown 副本。两种方案均可,本设计采用统一保存到 `static/uploads/posts//`,在 `posts_data` 中不再重复存储图片。 ## 4. 需要创建的文件清单 | 文件路径 | 作用 | |---------|------| | `app.py` | Flask 应用入口,定义所有路由(首页、上传、文章详情、管理、删除) | | `config.py` | 配置项:`UPLOAD_FOLDER`, `GENERATED_FOLDER`, `INDEX_FILE`, `ALLOWED_EXTENSIONS`, `MAX_CONTENT_LENGTH` 等 | | `requirements.txt` | 依赖列表(Flask, markdown, Pygments 等) | | `templates/base.html` | 全局模板,包含 HTML 骨架、导航栏、MathJax 脚本、基础 CSS/JS 引用 | | `templates/index.html` | 首页,继承 base.html,使用瀑布流卡片渲染文章列表 | | `templates/upload.html` | 上传表单,支持 Markdown 文件 + 多图片选择 | | `templates/admin.html` | 后台管理,以表格形式列出所有文章,提供删除按钮 | | `static/css/style.css` | 完整样式定义(瀑布流栅格、卡片样式、按钮、代码高亮等) | | `static/js/main.js` | 可选前端增强(如卡片悬停效果、动态瀑布流重排) | | `posts_index.json` | 运行时自动生成/更新,存储结构见下文 | ## 5. 核心功能设计 ### 5.1 文章上传与处理流程 1. **接收请求**:POST `/upload`,接收 `title`(字符串),`markdown_file`(文件),`images`(多文件列表)。 2. **创建文章 ID**:使用时间戳(如 `int(time.time()*1000)`)或 `uuid4` 生成唯一 `post_id`。 3. **创建目录**: - 图片保存目录:`static/uploads/posts//` - 原始 Markdown 保存目录:`posts_data//` 4. **保存文件**: - 将 Markdown 文件保存为 `posts_data//content.md`。 - 将每个图片保存到 `static/uploads/posts//` 下,保留原文件名(需过滤危险字符)。 5. **解析 Markdown**: - 读取 `content.md` 内容,提取 Front Matter(若存在,使用 `frontmatter` 库或正则),获得 `title`, `date`, `summary` 等。若 Front Matter 缺失,则使用表单中的 `title`,当前时间作为发布日期,摘要从正文前 200 字生成。 - 处理图片 URL:扫描 Markdown 中所有 `![](filename)` 式的本地图片引用,将 `filename` 替换为 `/static/uploads/posts//filename`。若图片未上传,记录警告但继续渲染。 6. **渲染 HTML**: - 使用 `markdown.markdown()` 并将扩展启用:`extra`, `codehilite`, `tables`, `fenced_code`。 - 若需 LaTeX 支持,在渲染结果中保留 `$...$` 和 `$$...$$`(不进行转义)。 7. **生成静态页面**: - 使用 Jinja2 模板(如 `post_template.html`,可从 `base.html` 继承)渲染完整 HTML,内容区为上述生成的 HTML,标题为文章标题。 - 保存到 `generated/.html`。 8. **更新索引**: - 读取 `posts_index.json`,新增一条记录:`{ "id": post_id, "title": title, "date": iso_date, "summary": summary, "thumbnail": 首张图片路径或空 }`。 - 按日期倒序排序后写回文件。 ### 5.2 列表页渲染(瀑布流) - 路由 `/` 读取 `posts_index.json`,将文章列表按日期倒序传入模板。 - 前端使用 CSS Grid 布局实现瀑布流: ```css .waterfall { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem; } .card { background: white; border-radius: 20px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); overflow: hidden; transition: transform 0.2s; } .card:hover { transform: translateY(-4px); } ``` - 每个卡片包含:文章标题、日期、摘要、若存在缩略图则显示在卡片顶部。缩略图可从索引的 `thumbnail` 字段获取。 ### 5.3 文章详情页 - 路由 `/post/` 使用 `send_from_directory` 返回 `generated` 目录下的 `.html`。 - 若文件不存在,返回 404 页面(可自定义模板)。 - 生成的静态页面中已包含 MathJax 脚本,正文中的 LaTeX 会被自动渲染。 ### 5.4 管理功能 - **管理页面**:`GET /admin`,读取索引展示所有文章,提供删除按钮(需注意 CSRF,可简单用 POST 表单)。 - **删除文章**:`POST /admin/delete/`,执行以下操作: - 删除 `generated/.html` - 删除 `posts_data//` 整个目录 - 删除 `static/uploads/posts//` 整个目录 - 从 `posts_index.json` 中移除该条目并保存 - 重定向回 `/admin` ## 6. 数据格式设计 ### 6.1 `posts_index.json` 结构 ```json [ { "id": "1703123456789", "title": "Python 中的装饰器", "date": "2023-12-21T10:30:00", "summary": "装饰器是 Python 一个强大的功能...", "thumbnail": "/static/uploads/posts/1703123456789/code.png" }, ... ] ``` ### 6.2 Markdown Front Matter 建议(可选) 若用户希望在 Markdown 文件头部定义元数据,支持以下格式: ```yaml --- title: 文章标题 date: 2024-01-01 summary: 自定义摘要 --- ``` 可使用 `python-frontmatter` 库解析,若未安装则回退到正则或忽略。在本设计中推荐使用,但非强制。 ## 7. 关键实现细节 ### 7.1 图片路径修正函数 在渲染 Markdown 之前,预处理文本: ```python def fix_image_paths(md_content: str, post_id: str) -> str: pattern = r'!\[([^\]]*)\]\(([^)]+)\)' def repl(match): alt, src = match.groups() # 仅当 src 不含 '/' 或 'http' 且扩展名为图片时视为本地文件 if not src.startswith(('http://', 'https://', '/', '#')) and '.' in src: new_src = f"/static/uploads/posts/{post_id}/{src}" return f'![{alt}]({new_src})' return match.group(0) return re.sub(pattern, repl, md_content) ``` ### 7.2 Markdown 渲染配置 ```python import markdown md = markdown.Markdown(extensions=[ 'extra', 'codehilite', 'tables', 'fenced_code' ]) html_body = md.convert(fixed_md) ``` ### 7.3 摘要提取(无 Front Matter 时) ```python from bs4 import BeautifulSoup soup = BeautifulSoup(html_body, 'html.parser') text = soup.get_text() summary = text[:200] + ('...' if len(text) > 200 else '') ``` ### 7.4 缩略图提取 优先使用 Front Matter 中指定的 `thumbnail`,否则取正文中第一张图片的 URL。 ## 8. 前端样式设计要点 ### 8.1 整体风格 - 背景色:`#f8fafc`(浅灰色),卡片纯白 `#ffffff`,圆角大半径(20px)。 - 字体:系统默认无衬线(-apple-system, BlinkMacSystemFont, "Segoe UI")。 - 导航栏:半透明白色毛玻璃效果,包含 “博客”、“上传”、“管理” 链接。 - 响应式:移动端禁用悬停效果,栅格 `minmax(250px,1fr)`。 ### 8.2 卡片内容 - 若缩略图存在:`` 宽度 100%,高度 180px,object-fit: cover。 - 标题:粗体,大小 1.25rem,上下边距。 - 日期:小字,灰色。 - 摘要:浅灰色文字,行高 1.5。 - 底部可加 “阅读全文 →” 链接,指向 `/post/`。 ### 8.3 代码高亮 使用 Pygments 生成 CSS 类,在 `style.css` 中引入一个默认高亮主题(如 `monokai` 或 `friendly`)。需在 Markdown 渲染时启用 `codehilite` 扩展并生成对应 HTML。 ### 8.4 MathJax 配置 在 `base.html` 中添加: ```html ``` 并设置 `tex: { inlineMath: [['$', '$'], ['\\(', '\\)']] }`。 ## 9. 路由概览 | 路径 | 方法 | 说明 | |------|------|------| | `/` | GET | 首页,瀑布流文章列表 | | `/upload` | GET | 显示上传表单 | | `/upload` | POST | 处理上传,保存文章并生成静态页 | | `/post/` | GET | 查看静态文章详情 | | `/admin` | GET | 管理页面,列出所有文章 | | `/admin/delete/` | POST | 删除文章及相关文件 | | `/static/` | GET | Flask 默认静态文件路由(用于 CSS、JS、图片) | ## 10. 部署与运行说明 1. 安装依赖:`pip install -r requirements.txt` 2. 运行:`python app.py`(默认监听 `0.0.0.0:5000`) 3. 使用生产服务器时,可将 `generated/` 目录通过 Nginx 直接托管提升性能,但 Flask 本身也可完成。 ## 11. 扩展建议 - 支持 RSS 订阅:从索引生成 XML。 - 添加文章标签系统:在索引中增加 `tags` 列表,前端做筛选。 - 缓存优化:列表页可定期重新生成静态文件。 - 图片压缩/缩略图生成:使用 Pillow 在上传时生成不同尺寸。 --- > 本设计文档已覆盖完整架构、目录结构、核心代码逻辑及样式取向。请后续根据本设计实现具体代码。