python使用Flask 3實(shí)現(xiàn)渲染指定目錄下Md文件
請用 python編程 : flask 3, mistune 2, python-multipart 實(shí)現(xiàn)同樣功能的Web服務(wù)
以下是使用 Flask 3 + mistune 2 + python-multipart 實(shí)現(xiàn)與 server2.js 相同功能的 Python Web 服務(wù),完整保留了原功能并適配 Python 生態(tài):
# -*- coding: utf-8 -*-
import os
from pathlib import Path
import mimetypes
import mistune
from mistune.renderers import HTMLRenderer
from flask import Flask, request, jsonify, send_file, send_from_directory, make_response
from werkzeug.utils import secure_filename
# 初始化 Flask 應(yīng)用
app = Flask(__name__, static_folder='static', static_url_path='/')
# ====================== 配置項(xiàng) ======================
# 默認(rèn)MD文件目錄
DEFAULT_MD_DIR = Path(__file__).parent / 'md-files'
# 確保默認(rèn)目錄存在
DEFAULT_MD_DIR.mkdir(parents=True, exist_ok=True)
print(f"創(chuàng)建/使用默認(rèn)MD文件目錄: {DEFAULT_MD_DIR.absolute()}")
# 允許上傳的文件類型
ALLOWED_EXTENSIONS = {'.md', '.markdown'}
# 文件大小限制 (5MB)
MAX_CONTENT_LENGTH = 5 * 1024 * 1024
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
# ====================== 安全工具函數(shù) ======================
def validate_and_normalize_dir(dir_path: str) -> Path:
"""驗(yàn)證并規(guī)范化目錄路徑"""
# 如果為空,返回默認(rèn)目錄
if not dir_path or dir_path.strip() == '':
return DEFAULT_MD_DIR.absolute()
# 解析為絕對路徑
abs_path = Path(dir_path.strip()).absolute()
# 檢查目錄是否存在
if not abs_path.exists():
raise ValueError(f"目錄不存在: {abs_path}")
# 檢查是否是目錄(不是文件)
if not abs_path.is_dir():
raise ValueError(f"不是有效的目錄: {abs_path}")
# 檢查目錄是否可讀
if not os.access(abs_path, os.R_OK):
raise ValueError(f"沒有目錄讀取權(quán)限: {abs_path}")
return abs_path
def allowed_file(filename: str) -> bool:
"""檢查文件擴(kuò)展名是否合法"""
return Path(filename).suffix.lower() in ALLOWED_EXTENSIONS
# ====================== 配置 mistune 渲染器(支持 mermaid) ======================
class MermaidRenderer(HTMLRenderer):
"""自定義渲染器,支持 mermaid 語法"""
def block_code(self, code: str, info: str = '') -> str:
if info and info.strip() == 'mermaid':
return f'<div class="mermaid">[code]</div>'
# 調(diào)用父類默認(rèn)實(shí)現(xiàn)
return super().block_code(code, info)
# 創(chuàng)建 mistune 實(shí)例
md_renderer = MermaidRenderer()
md_parser = mistune.create_markdown(renderer=md_renderer,
plugins=['strikethrough', 'table', 'url'],
escape=False # 關(guān)鍵配置:禁用字符轉(zhuǎn)義
)
# ====================== 讀取靜態(tài) HTML 模板(緩存) ======================
def get_index_html() -> str:
"""讀取并緩存 index2.html 內(nèi)容"""
index_path = Path(__file__).parent / 'static' / 'index2.html'
if not index_path.exists():
raise FileNotFoundError(f"index2.html 不存在: {index_path}")
with open(index_path, 'r', encoding='utf-8') as f:
return f.read()
index_html = get_index_html()
# ====================== 接口:獲取MD文件列表(支持自定義目錄) ======================
@app.route('/get-md-files', methods=['GET'])
def get_md_files():
try:
# 獲取并驗(yàn)證目錄路徑
dir_path = validate_and_normalize_dir(request.args.get('dirPath', ''))
# 讀取目錄下所有.md文件(排除隱藏文件)
files = []
for file in dir_path.iterdir():
if file.is_file() and allowed_file(file.name) and not file.name.startswith('.'):
files.append(file.name)
# 按名稱排序
files.sort()
return jsonify(files)
except ValueError as e:
app.logger.error(f"讀取文件列表失敗: {e}")
return jsonify({'error': str(e)}), 400
except Exception as e:
app.logger.error(f"讀取文件列表異常: {e}")
return jsonify({'error': f'服務(wù)器內(nèi)部錯(cuò)誤: {str(e)}'}), 500
# ====================== 接口:加載指定MD文件并渲染(支持自定義目錄) ======================
@app.route('/load-md-file', methods=['GET'])
def load_md_file():
try:
# 驗(yàn)證文件名參數(shù)
filename = request.args.get('filename', '')
if not filename or '..' in filename or not allowed_file(filename):
return jsonify({'error': '無效的文件名'}), 400
# 解碼文件名(處理URL編碼)
filename = request.args.get('filename', '').encode('utf-8').decode('unicode_escape')
# 獲取并驗(yàn)證目錄路徑
dir_path = validate_and_normalize_dir(request.args.get('dirPath', ''))
file_path = dir_path / secure_filename(filename)
# 檢查文件是否存在
if not file_path.exists() or not file_path.is_file():
return jsonify({'error': f'文件不存在: {filename}'}), 404
# 檢查文件是否可讀
if not os.access(file_path, os.R_OK):
return jsonify({'error': f'沒有文件讀取權(quán)限: {filename}'}), 403
# 讀取文件內(nèi)容并渲染為HTML
with open(file_path, 'r', encoding='utf-8') as f:
md_content = f.read()
rendered_html = md_parser(md_content)
return rendered_html
except ValueError as e:
app.logger.error(f"加載MD文件失敗: {e}")
return jsonify({'error': str(e)}), 400
except Exception as e:
app.logger.error(f"加載MD文件異常: {e}")
return jsonify({'error': f'服務(wù)器內(nèi)部錯(cuò)誤: {str(e)}'}), 500
# ====================== 原有接口 ======================
# 1. 首頁:返回上傳頁面
@app.route('/')
def index():
return send_file(Path(__file__).parent / 'static' / 'index2.html')
# 2. 處理 Markdown 文件上傳 + 解析 + 渲染
@app.route('/upload-md', methods=['POST'])
def upload_md():
try:
# 檢查是否有文件上傳
if 'mdFile' not in request.files:
return '<h1>錯(cuò)誤</h1><p>請選擇要上傳的 Markdown 文件!</p>', 400
file = request.files['mdFile']
# 檢查文件名是否為空
if file.filename == '':
return '<h1>錯(cuò)誤</h1><p>請選擇要上傳的 Markdown 文件!</p>', 400
# 檢查文件類型
if file and allowed_file(file.filename):
# 讀取文件內(nèi)容
md_content = file.read().decode('utf-8')
# 渲染為HTML
rendered_html = md_parser(md_content)
# 替換模板中的占位符
final_html = index_html.replace('<div id="markdown-content"></div>', rendered_html)
response = make_response(final_html)
response.headers['Content-Type'] = 'text/html; charset=utf-8'
return response
# 文件類型不允許
return '<h1>錯(cuò)誤</h1><p>僅支持上傳 .md 或 .markdown 格式的文件!</p>', 400
except Exception as e:
app.logger.error(f"處理上傳文件出錯(cuò): {e}")
error_html = f"""
<h1>出錯(cuò)了</h1>
<p>處理上傳的 Markdown 文件失?。簕str(e)}</p>
<a href="/" rel="external nofollow" >返回上傳頁面</a>
"""
return error_html, 500
# 靜態(tài)文件服務(wù)(兼容原邏輯)
@app.route('/<path:filename>')
def serve_static(filename):
return send_from_directory(app.static_folder, filename)
# ====================== 啟動(dòng)服務(wù) ======================
if __name__ == '__main__':
# 注冊 mimetypes(確保js文件正確解析)
mimetypes.add_type('application/javascript', '.js')
print(f"服務(wù)已啟動(dòng):http://localhost:8000")
print(f"默認(rèn)MD文件目錄:{DEFAULT_MD_DIR.absolute()}")
print(f"訪問 http://localhost:8000/index2.html 即可瀏覽和渲染MD文件")
# 啟動(dòng)Flask服務(wù)(調(diào)試模式,生產(chǎn)環(huán)境請關(guān)閉)
app.run(host='127.0.0.1', port=8000, debug=True)
配套依賴安裝
創(chuàng)建 requirements.txt 文件:
Flask==3.0.3 mistune==2.0.5 python-multipart==0.0.6 Werkzeug==3.0.3
安裝依賴:
pip install -r requirements.txt
關(guān)鍵說明(與原 Node.js 版本對齊)
功能完全對齊:
- 支持自定義目錄加載 MD 文件列表
- 加載并渲染指定 MD 文件(支持 Mermaid 圖表)
- 上傳 MD 文件并實(shí)時(shí)渲染
- 路徑安全校驗(yàn)(防止路徑遍歷、權(quán)限檢查)
- 文件類型/大小限制
核心差異適配:
- Node.js 的
markdown-it→ Python 的mistune(自定義渲染器支持 Mermaid) - Node.js 的
multer→ Flask 原生 +python-multipart處理文件上傳 - Node.js 的
fs/path→ Python 的pathlib/os處理文件系統(tǒng) - 錯(cuò)誤處理、響應(yīng)格式完全對齊原接口
部署注意事項(xiàng):
- 需要保持原有的
static目錄結(jié)構(gòu)(包含index2.html和mermaid.min.js) md-files目錄會(huì)自動(dòng)創(chuàng)建,用于存放默認(rèn) MD 文件- 生產(chǎn)環(huán)境請關(guān)閉
debug=True,并使用 Gunicorn 等 WSGI 服務(wù)器運(yùn)行
目錄結(jié)構(gòu)要求
TREE /F
md-mermaid
├── server.py # Python 服務(wù)文件
├── static/ # 靜態(tài)文件目錄
│ ├── index2.html # 前端頁面(復(fù)用原文件)
│ └── mermaid.min.js # Mermaid 渲染腳本
├── md-files/ # 默認(rèn) MD 文件目錄(自動(dòng)創(chuàng)建)
└── requirements.txt # 依賴文件
運(yùn)行方式
python server.py
訪問 http://localhost:8000 即可使用,功能與原 Node.js 版本完全一致。
以上就是python使用Flask 3實(shí)現(xiàn)渲染指定目錄下Md文件的詳細(xì)內(nèi)容,更多關(guān)于python渲染Md文件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于python實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于python學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
java中兩個(gè)byte數(shù)組實(shí)現(xiàn)合并的示例
今天小編就為大家分享一篇java中兩個(gè)byte數(shù)組實(shí)現(xiàn)合并的示例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05
python根據(jù)json數(shù)據(jù)畫疫情分布地圖的詳細(xì)代碼
這篇文章主要介紹了python根據(jù)json數(shù)據(jù)畫疫情分布地圖的詳細(xì)代碼,掌握使用pyecharts構(gòu)建基礎(chǔ)的全國地圖可視化圖表,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
Python實(shí)現(xiàn)Tracert追蹤TTL值的方法詳解
Tracert命令跟蹤路由原理是IP路由每經(jīng)過一個(gè)路由節(jié)點(diǎn)TTL值會(huì)減一。本文我們將通過scapy構(gòu)造一個(gè)路由追蹤工具并實(shí)現(xiàn)一次追蹤,感興趣的小伙伴可以了解一下2022-10-10
python基于Tkinter實(shí)現(xiàn)人員管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python基于Tkinter實(shí)現(xiàn)人員管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
Flask入門教程實(shí)例:搭建一個(gè)靜態(tài)博客
這篇文章主要介紹了Flask入門教程實(shí)例:搭建一個(gè)靜態(tài)博客,本文主要介紹flask框架的環(huán)境配置以及一個(gè)靜態(tài)博客胡搭建實(shí)例,需要的朋友可以參考下2015-03-03

