Flask + Editor.md 在线 Markdown 编辑器实战:自动保存与 sm.ms 图床集成

发布时间:2026/9/30 2:16:32

Flask + Editor.md 在线 Markdown 编辑器实战:自动保存与 sm.ms 图床集成 简介这是一套基于 Python、Flask 与 Editor.md 构建的在线 Markdown 编辑工具源码面向具备 Flask 基础、希望学习或直接搭建在线编辑平台的开发者。项目整合了 Flask-SQLAlchemy、Flask-Login 与 sm.ms 图床实现登录注册、文章编辑与文章列表三个核心页面并支持自动保存与图片上传至图床等功能适合作为 Web 开发练手项目或轻量级写作平台的基础框架。压缩包共 559 个文件约 15.07MB以 208 个 js、170 个 html、60 个 css 等前端资源为主另有 14 个 py 后端文件、8 个 md 说明文档及图片、字体、配置等辅助文件并附有 readme.md 教程。目前已有 447 人学习下载。通过阅读源码与目录结构读者可掌握 Flask 项目分层组织、用户认证、数据库建模、Markdown 渲染及第三方图床对接等完整实现思路对理解前后端协作与快速搭建同类工具具有较高参考价值。1. 在线 Markdown 编辑工具从压缩包到能跑起来的 Flask 应用拿到一个压缩包解压后看到editormd.min.css、bootstrap.min.css、flask-sqlalchemy这些文件名混在一起很多人第一反应是「这到底是个能直接跑的项目还是一堆前端素材」。这个资源是一个基于 Python Flask Editor.md 的在线 Markdown 编辑工具后端用 Flask 和 Flask-SQLAlchemy 管数据前端用 Editor.md 做编辑区图片上传走 sm.ms 图床数据库默认 SQLite。它包含登录注册、编辑、文章列表三个页面支持自动保存和图片上传。适合谁对 Flask 有基本了解、想找一个能直接改的在线编辑器骨架的人。如果你连pip install和flask run都没用过这个包会让你卡在环境配置上但如果你写过最简单的 Flask 路由这个项目能让你在半小时内看到一个带登录和自动保存的编辑器跑在本地。它解决的不是「Markdown 语法怎么用」而是「怎么把 Editor.md 嵌进 Flask、怎么让编辑内容自动落库、怎么把本地图片传到图床」这三个工程问题。2. 拆包与选型为什么是 Flask Editor.md sm.ms2.1 压缩包里的文件到底谁管谁解压后先别急着装依赖把文件按职责分三类后面排错会快很多。文件/目录职责是否可替换AUTHORS作者信息不影响运行可忽略default.cfg/self.cfg配置模板与个人配置需按环境改bootstrap.min.css/bootstrap-theme.min.css页面基础样式可换版本editormd.css/editormd.min.cssEditor.md 编辑区样式不建议动editormd.preview.css/editormd.preview.min.css预览区样式不建议动ambiance.css代码高亮主题可换主题readme.md安装教程先读default.cfg和self.cfg是 Flask 常见的配置分离做法前者放默认值后者放本机敏感值比如数据库路径、图床 token。如果你只改default.cfg换台机器就会丢配置正确做法是复制一份self.cfg再改。2.2 为什么选 Editor.md 而不是手写 textareaMarkdown 在线编辑器的核心需求是「左边写、右边实时预览」。手写一个 textarea 加marked.js也能预览但表格、流程图、数学公式、代码高亮这些都要自己接。Editor.md 把这些打包好了还提供onchange回调自动保存直接挂在这个回调上就行。代价是 Editor.md 依赖 jQuery且它的 CSS 和 JS 必须按顺序引入。常见翻车是只引了editormd.min.js没引editormd.min.css编辑区能显示但工具栏错位。另一个坑是editormd.preview.css和editormd.css同时引入时顺序反了预览区代码块背景会丢。2.3 sm.ms 图床的接入逻辑图片上传不是把文件存本地而是前端选图后发给 FlaskFlask 再转发到 sm.ms 的 API拿到返回的 URL 后插入编辑器。这样做的好处是文章里存的是外链数据库只存 Markdown 文本体积小。代价是依赖第三方图床可用性且 sm.ms 需要 token 才能稳定上传。常见做法是在self.cfg里放一个SM_MS_TOKENFlask 上传接口读这个值。如果你没有 token匿名上传也能用但频率受限批量传图会失败。3. 把项目跑起来环境、配置与三个页面3.1 环境准备与依赖安装先确认 Python 版本。Flask-SQLAlchemy 和 Flask-Login 对 Python 3.7 支持稳定建议用 3.8 到 3.10。不要用 3.12 直接跑老版本 Flask部分依赖会编译失败。# 创建虚拟环境避免污染系统 Python python -m venv venv # 激活Linux/macOS source venv/bin/activate # 激活Windows venv\Scripts\activate # 安装核心依赖版本按压缩包 readme 为准 pip install flask flask-sqlalchemy flask-login requests逻辑说明venv隔离依赖flask是 Web 框架flask-sqlalchemy管 ORMflask-login管会话requests用于转发图片到 sm.ms。参数上如果 readme 里给了requirements.txt优先用pip install -r requirements.txt避免版本漂移。3.2 配置文件怎么改打开default.cfg通常会看到类似结构# default.cfg 示例结构字段名以实际文件为准 DEBUG True SQLALCHEMY_DATABASE_URI sqlite:///site.db SECRET_KEY change-me SM_MS_TOKEN 复制一份为self.cfg改三个地方SECRET_KEY换成随机字符串SQLALCHEMY_DATABASE_URI确认路径可写SM_MS_TOKEN填你自己的 sm.ms token。DEBUG在本地调试时保持True上线前改False。提示SECRET_KEY不换会导致 Flask-Login 的会话可被伪造本地玩无所谓放到公网就是血泪经验。3.3 初始化数据库与启动# 进入项目目录后初始化数据库表 python -c from app import db; db.create_all() # 启动 Flask flask run # 或 python app.py逻辑说明db.create_all()会根据模型定义建表SQLite 会在项目目录生成.db文件。如果报no such table说明模型没被导入或create_all没执行。启动后访问http://127.0.0.1:5000应该看到登录页。三个页面的路由通常是/login、/register、/edit、/list。登录后进编辑页Editor.md 加载完成后工具栏出现文章列表页从数据库读当前用户的文章。3.4 自动保存挂在哪自动保存一般挂在 Editor.md 的onchange回调上配合setInterval做节流。核心逻辑是编辑内容变化后每隔几秒把 Markdown 文本 POST 到/save接口后端按文章 ID 更新记录。// 编辑页自动保存逻辑示意 var editor editormd(editor, { path: /static/editormd/lib/, onchange: function() { // 内容变化时标记为未保存 window.unsaved true; } }); // 每 5 秒检查一次有变化才提交 setInterval(function() { if (window.unsaved) { $.post(/save, { id: articleId, content: editor.getValue() }, function(res) { window.unsaved false; }); } }, 5000);参数说明path必须指向 Editor.md 的lib目录否则依赖的 marked、prettify 加载不到5000是节流间隔太小会增加请求太大丢内容风险高。后端/save接口要用flask-login的login_required保护并校验文章归属否则任何人改 ID 就能改别人文章。4. 图片上传与 sm.ms 图床对接从选图到插入链接4.1 前端上传入口Editor.md 自带图片上传按钮但默认是弹 URL 输入框。要接自己的上传接口需要配置imageUpload和imageFormats。editormd(editor, { path: /static/editormd/lib/, imageUpload: true, imageFormats: [jpg, jpeg, gif, png, bmp, webp], imageUploadURL: /upload, onload: function() { // 编辑器加载完成后的初始化 } });逻辑说明imageUploadURL指向 Flask 的上传路由Editor.md 会把文件以multipart/form-data发过去字段名通常是editormd-image-file。后端必须返回 JSON格式为{success: 1, url: 图片地址}否则编辑器会提示上传失败。4.2 Flask 端转发到 sm.msimport requests from flask import request, jsonify from flask_login import login_required login_required def upload(): file request.files.get(editormd-image-file) if not file: return jsonify({success: 0, message: 没有文件}) # 读取 token没有则匿名上传 token current_app.config.get(SM_MS_TOKEN, ) headers {} if token: headers[Authorization] token files {smfile: (file.filename, file.stream, file.mimetype)} resp requests.post( https://sm.ms/api/v2/upload, filesfiles, headersheaders, timeout15 ) data resp.json() if data.get(success): return jsonify({success: 1, url: data[data][url]}) # 重复上传时 sm.ms 返回 images 字段 if data.get(code) image_repeated: return jsonify({success: 1, url: data[images]}) return jsonify({success: 0, message: data.get(message, 上传失败)})参数说明timeout15防止请求挂死Authorization头在有 token 时带上能提高配额image_repeated是 sm.ms 对同一张图重复上传的返回直接复用已有 URL避免报错。注意file.stream在 Flask 里可直接传给 requests不要先file.read()再传大图会占内存。4.3 上传失败的排查顺序先看浏览器 Network 里/upload的响应如果是 302 跳登录说明login_required生效但会话丢了如果是 500看 Flask 控制台报错如果返回success: 0看message字段。sm.ms 常见错误是 token 无效或频率超限换 token 或等几分钟再试。5. 避坑与常见问题登录、自动保存、图床的翻车记录5.1 登录后立刻跳回登录页现象输入账号密码后页面刷新又回到/login。原因通常是SECRET_KEY没设置或每次重启变化导致 Flask-Login 的 session 无法解码。解决在self.cfg里写死一个随机字符串确保每次启动一致。5.2 自动保存把空内容覆盖了原文现象打开编辑页还没动几秒后文章变空。原因是onchange在编辑器初始化时被触发unsaved被置为true而getValue()此时返回空。解决在onload回调里先把unsaved设为false或者加一个「内容长度大于 0 才提交」的判断。5.3 图片上传成功但编辑器不显示现象Network 里/upload返回了 URL但编辑区没插入图片。原因是返回 JSON 的字段名不对Editor.md 要求success为 1 且url存在。如果后端返回的是{code: 0, data: {...}}编辑器不认。解决严格按{success: 1, url: ...}返回。5.4 SQLite 数据库锁死现象多人同时保存时偶尔报database is locked。SQLite 默认写锁是串行的并发高就排队。解决本地单人用没问题如果要多人用换 PostgreSQL 或 MySQL改SQLALCHEMY_DATABASE_URI即可ORM 层不用动。5.5 Editor.md 工具栏图标不显示现象编辑区能写但工具栏是一排空白按钮。原因是editormd.min.css没引入或者path配错导致字体文件 404。解决检查 HTML 里 CSS 引入顺序确认path指向的lib目录下有fonts文件夹。6. 进阶把自动保存做成可恢复的草稿机制自动保存只解决「不丢」不解决「改错了想回退」。我一般会在保存接口里加一个草稿表每次保存前把旧内容写进draft表保留最近 10 个版本。这样即使自动保存覆盖了也能从数据库里捞回上一版。# 保存时先写草稿再更新正文 def save_article(article_id, new_content): article Article.query.get(article_id) # 旧内容存入草稿表 draft Draft(article_idarticle_id, contentarticle.content) db.session.add(draft) article.content new_content db.session.commit() # 只保留最近 10 条草稿 drafts Draft.query.filter_by(article_idarticle_id)\ .order_by(Draft.id.desc()).offset(10).all() for d in drafts: db.session.delete(d) db.session.commit()参数说明offset(10)表示跳过最新 10 条删掉更早的控制表体积。这个逻辑放在保存接口里对前端透明。验证方法是手动改几次内容然后查draft表看是否有历史记录。另一个进阶点是 Markdown 换行和表格的预览一致性。Editor.md 默认遵循 CommonMark单个换行不产生br如果你习惯 Typora 的换行行为需要在初始化时配breaks: true。表格转换 Excel 这类需求不在编辑器内做导出后用脚本处理更稳。从那以后我每次接在线编辑器都强制先跑一遍「登录 → 编辑 → 自动保存 → 刷新恢复 → 传图」这条链路确认没有玄学丢内容才继续改功能。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/30 2:16:32

DRAM刷新机制详解:集中式、分散式与异步式刷新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 2:16:32

Windows单网卡双IP路由配置实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 3:21:36

Ubuntu 软件安装避坑指南:apt、deb、Snap 与源码编译

1. 为什么“装个软件”这件小事,在 Ubuntu 上能难倒一大批人我见过太多刚转到 Ubuntu 的人,第一反应都是抱着 Windows 的思维习惯:去浏览器搜“某某软件下载”,点进去找一个 exe 或者 dmg 文件,然后双击、下一步、完成…

2026/9/30 3:21:36

CKA备考:彻底理清ClusterRole与ClusterRoleBinding权限配置

CKA倒计时第24天,今天把RBAC里最容易混淆的一组概念彻底理清:ClusterRole和ClusterRoleBinding。为什么单独拿一天来写这对组合?因为CKA考试里权限相关题目几乎必考,而且大概率不是单纯考Role,而是考集群级授权。我考前…

2026/9/30 3:21:36

VMware安装Ubuntu 22.04完整指南:从激活到开发环境搭建

1. 为什么非得在VMware里装Ubuntu?——从“能跑”到“好用”的真实分水岭你搜“VMware安装Ubuntu”,大概率不是为了凑个Linux界面截图交差,而是真要干活:写Python脚本调试环境、跑Docker容器做微服务测试、搭本地GitLab练DevOps、…

2026/9/30 3:21:36

Docker host网络模式避坑指南:3大坑与4个核心要点全解析

Docker的host网络模式,听名字挺直白,就是把容器直接塞进宿主机的网络栈里。很多新手第一次用的时候,总觉得它会比bridge高级,结果一上手就被各种诡异问题打懵。我今天把最常见的3个坑和4个核心要点一次说清楚,也算给自…

2026/9/30 3:21:36

Linux 文件传输实战:scp、rsync、netcat 三大工具对比与脚本

在两台 Linux 机器之间传文件,这个需求听着基础,真做起来门道不少。绝大多数人最先想到的是 scp,一条命令就能把文件推到另一台机器上;可当你需要同步几十 GB 的目录、或者机器之间只有临时网络环境、再或者 SSH 服务没开而你也没…

2026/9/30 3:16:35

漫无目的刷算法题:从简单题开始,反而成长更快

看到这个标题的时候我乐了。这不就是我的日常吗——晚上九点半洗完澡坐电脑前,本来只想看一眼明天的天气,结果鬼使神差打开算法题库,随手点开一道“简单”,就开始刷了。没有计划表,没有目标清单,纯粹是想动…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 0:01:22

MATLAB+Yalmip+CPLEX实战:综合能源系统优化调度全流程解析

做综合能源系统优化调度这活儿,最痛苦的不是建模本身,而是模型写完之后不知道该怎么求解。看论文里轻飘飘一句“采用Yalmip调用CPLEX求解”,自己上手时却往往卡在环境配置、变量声明、约束写法和求解状态判读上,一耗就是两三天。这…

2026/9/30 0:01:22

I3C比I2C快10倍?RK3576实战:速率、DTS配置与混合总线避坑指南

I3C 比 I2C 快 10 倍?这句话在嵌入式群里传了很久,每次都能吵出一堆截图。前段时间我正好在 RK3576 上调板级 I3C 接口,从控制器寄存器一路摸到 Linux DTS 配置,踩了不少坑,也把这笔速度账彻底算明白了。本文就用 RK35…

2026/9/30 0:01:22

字符串转对象:JSON.parse、new Function与URLSearchParams

“字符串转对象”这几个字,我在技术群里见过的问法至少有十几种:有人拿着一串{a:1,b:2}说 JSON.parse 直接报错,有人要从 URL 里抠出参数,还有人只是想把abc变成能挂属性的东西。js 这门语言里,字符串和对象之间的转换…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑