FTP协议深度解析:从双通道原理到现代安全实践
1. 从“文件传输”说起FTP到底是什么如果你在互联网行业待过几年或者自己折腾过网站、服务器那么“FTP”这个词你肯定不陌生。它就像一个互联网世界里的“老邮差”虽然现在有了更多更快更安全的“快递公司”但这个老邮差依然在很多角落里默默工作着。今天我们就来彻底聊聊这个“老邮差”——FTP。FTP全称是 File Transfer Protocol中文叫“文件传输协议”。这个名字拆开来看就非常直白“文件传输”是它的核心功能“协议”则说明了它是一套标准化的规则。简单来说FTP就是一套专门用来在网络上从一台计算机向另一台计算机传输文件的“交通规则”。这套规则定义了客户端比如你的电脑和服务器端比如存放你网站文件的服务器之间如何建立连接、如何验证身份、如何发送命令、以及最终如何传输文件数据。为什么我们需要这样一个专门的协议呢在互联网的早期网络环境复杂文件类型多样大小不一如果没有一个统一的“语言”来沟通传输文件就会变得混乱不堪。FTP协议的出现就是为了解决这个“沟通”问题。它让不同操作系统比如Windows、Linux、macOS上的不同软件只要都遵循FTP协议就能无障碍地进行文件交换。想象一下你有一台Windows电脑需要把网站代码上传到一台Linux服务器上你不需要关心服务器内部是怎么处理文件的只需要通过一个支持FTP的客户端软件比如FileZilla输入服务器的地址、账号和密码就能像操作本地文件夹一样把文件拖拽过去。这种便利性是FTP能流行几十年的根本原因。2. FTP协议的核心工作机制双通道的智慧要理解FTP不能只停留在“上传下载”的表面操作还得看看它底层的运作机制。FTP一个非常经典且核心的设计就是它的“双通道”模型。这和我们平时浏览网页使用HTTP协议有很大不同。2.1 控制连接与数据连接FTP使用两条独立的TCP连接来完成任务控制连接Control Connection这是客户端和服务器之间的“指挥通道”。默认使用TCP的21号端口。所有你发出的命令比如LIST查看目录、CWD切换目录、RETR下载文件、STOR上传文件和服务器返回的响应状态码比如200命令成功、550文件不可用都通过这条连接传输。这条连接在整个FTP会话期间会一直保持打开状态直到你退出登录。数据连接Data Connection这是真正的“货物运输通道”。它专门用于传输文件内容或者目录列表。这条连接是临时的只有在需要传输数据时才会建立传输完成后立即关闭。下次需要传输时再重新建立一条新的数据连接。这种分离的设计非常巧妙。控制连接始终保持确保了会话的连续性和命令的实时交互数据连接随用随建用完即关避免了长期占用网络资源也使得可以同时管理多个数据传输任务虽然传统FTP本身不直接支持并行传输但客户端可以管理多个数据连接。2.2 主动模式与被动模式谁主动发起数据连接数据连接的建立方式是FTP配置和使用中最容易让人困惑的地方也直接关系到你能否成功连接服务器。这里的关键在于由谁来发起建立数据连接的请求。主动模式PORT Mode 在主动模式下建立数据连接的“主动权”在服务器手里。过程是这样的客户端通过21端口连接到服务器建立控制连接。当客户端需要传输数据如下载文件时它会通过控制连接告诉服务器“我将在本地的IP:端口例如192.168.1.100:6000上监听请你主动连接过来。”服务器收到指令后会从自己的20号端口默认数据端口主动发起连接到客户端指定的IP:端口从而建立数据连接。注意主动模式在现代网络环境下问题很大。因为客户端通常位于路由器或防火墙后面拥有私有IP服务器从公网无法直接访问客户端指定的那个私有IP和端口。这会导致服务器连接失败出现“无法建立数据连接”的错误。被动模式PASV Mode 为了解决主动模式的问题被动模式被广泛采用。在被动模式下建立数据连接的“主动权”交给了客户端。同样先建立控制连接。当客户端需要传输数据时它会向服务器发送PASV命令。服务器收到PASV命令后会在自己的某个空闲端口比如30000上开启监听然后通过控制连接告诉客户端“我已经在IP:端口例如203.0.113.10:30000上准备好了你连接过来吧。”客户端随后主动发起连接到服务器告知的这个IP:端口建立数据连接。由于是客户端主动去连接服务器的某个端口这就完美避开了客户端防火墙的入站拦截问题。因此在绝大多数情况下尤其是在家庭网络或企业内网作为客户端时都应该使用被动模式PASV。现在主流的FTP服务器软件和客户端软件默认也通常采用被动模式。理解这两种模式是解决FTP连接故障尤其是能列出目录但无法传输文件的关键。当你遇到问题时第一反应就应该是去客户端软件里检查或切换传输模式。3. FTP的典型应用场景老将仍在发光发热尽管FTP年事已高且存在一些明显的安全缺陷我们后面会讲但它并没有被完全淘汰。在一些特定的、对实时性要求不高、且处于相对可控网络环境下的场景中FTP因其简单、通用、客户端支持广泛的特性依然扮演着重要角色。1. 网站内容维护与部署这是FTP最经典的应用。很多虚拟主机服务商仍然提供FTP账号来管理网站空间。开发者使用FileZilla、WinSCP等工具可以直观地将本地开发好的HTML、CSS、JavaScript、图片等文件上传到服务器的指定目录如/www或/htdocs。对于小型网站或静态网站这种方式简单直接。2. 企业内部或合作伙伴间的大文件共享在一些企业内网环境中需要定期共享大型的报表、设计图纸、视频素材等文件。搭建一个内部FTP服务器设置不同的账号和目录权限是一种成本较低的解决方案。相比于依赖云盘内网FTP在传输速度和数据可控性上可能有优势。3. 软件与文档的分发一些开源软件项目、硬件厂商的驱动和固件仍会提供FTP下载链接作为备选方案。因为FTP协议简单几乎所有的操作系统都内置了命令行FTP客户端如ftp命令确保了最大程度的兼容性。4. 设备数据采集与备份在一些工业或物联网场景中旧的设备或系统可能只支持FTP协议进行数据导出。例如网络摄像头将录制视频定时推送到FTP服务器或者旧的数据库系统将备份文件自动上传到FTP服务器。5. 作为其他应用的基础服务在开发测试环境中FTP也常被使用。例如在docker部署ftp服务器的场景中开发者可以快速在容器里拉起一个FTP服务用于测试文件上传下载功能而无需污染宿主机环境。像pb 实现ftp这里可能指PowerBuilder编程实现FTP客户端这样的需求也说明了在特定遗留业务系统中集成FTP功能的情况依然存在。4. FTP的安全短板与现代替代方案谈FTP就不能不谈它的“阿喀琉斯之踵”——安全性。传统FTP协议在设计之初网络环境相对单纯并未充分考虑安全问题这导致了几个致命的缺陷明文传输这是最大的问题。FTP在传输过程中包括用户名、密码、所有命令以及文件数据全都是明文未加密。这意味着任何能够截获你网络流量的人比如在公共Wi-Fi上都可以轻而易举地看到你的登录凭证和传输的文件内容。这绝对不适合传输任何敏感信息。认证方式单一主要依赖用户名和密码缺乏多因素认证等更安全的手段。被动模式的端口随机性虽然方便了客户端但给服务器端防火墙配置带来了麻烦。服务器需要开放一个较大范围的端口如30000-30010供被动模式使用增加了被攻击的面。正因为这些安全问题在公开的互联网环境中纯FTP已经不被推荐用于任何严肃的、涉及敏感数据的传输。4.1 安全的演进FTPS与SFTP为了弥补FTP的安全缺陷出现了两种主流的增强或替代协议FTPS (FTP over SSL/TLS)你可以把它理解为“加密版的FTP”。它在标准FTP协议的基础上增加了SSL/TLS加密层就像HTTP升级为HTTPS一样。FTPS可以对控制连接和数据连接都进行加密确保整个会话的机密性。它有两种模式隐式SSL使用默认的990端口和显式SSL先建立普通连接再通过AUTH TLS命令升级为加密连接。FTPS是FTP的直接安全扩展兼容原有工作模式但配置稍复杂。SFTP (SSH File Transfer Protocol)请注意SFTP虽然名字里有FTP但它和FTP协议完全不同SFTP是SSHSecure Shell协议的一部分它通过SSH的安全加密通道来传输文件。因为基于SSH所以它天然支持加密、强认证如密钥登录、并且通常只使用一个端口SSH的22端口极大简化了防火墙配置。SFTP在功能上可以完全替代FTP并且更安全、更现代是当前首选的方案。像WinSCP、FileZilla等客户端都同时支持FTP和SFTP。实操心得现在新建项目或服务如果需要在网络上传输文件应优先选择SFTP。如果必须维护一个旧的FTP服务务必将其升级为FTPS并禁用普通的FTP连接。在linux服务器ftp配置中像vsftpd、ProFTPD等主流软件都支持FTPS务必在配置文件中启用SSL/TLS。4.2 为什么我们还在讨论FTP既然有更安全的SFTP为什么还要了解FTP呢原因有几个历史遗留系统大量的老旧设备、软件和业务流程仍然依赖FTP作为技术人员你需要会维护和对接。概念基础理解FTP的双通道、主动/被动模式是理解很多网络协议和文件传输问题的基础。排错能力当遇到“能登录但传不了文件”这类经典问题时懂FTP原理能让你快速定位到是防火墙、还是传输模式配置错误。广泛的支持几乎任何平台、任何语言都有现成的FTP客户端库实现一个简单的文件传输功能非常快速。5. 动手实践从搭建到连接的全过程解析了解了原理我们通过一个简单的实践来串联这些知识。假设我们要在一台Linux服务器上搭建一个FTP服务并从Windows客户端进行连接。这里我们选择常用的vsftpdVery Secure FTP Daemon作为服务器软件。5.1 Linux服务器端搭建以CentOS/RHEL为例步骤1安装vsftpdsudo yum install vsftpd -y # CentOS/RHEL # 或 sudo apt-get install vsftpd -y # Ubuntu/Debian步骤2配置vsftpd配置文件通常位于/etc/vsftpd/vsftpd.conf。我们需要修改几个关键参数sudo vi /etc/vsftpd/vsftpd.conf找到并修改或确保以下行# 允许本地用户登录 local_enableYES # 允许写入操作上传、删除 write_enableYES # 设置本地用户的默认文件掩码022表示创建的文件权限为755目录和644文件 local_umask022 # 禁止匿名登录提高安全性 anonymous_enableNO # 被动模式配置关键 # 启用被动模式 pasv_enableYES # 设置服务器用于被动模式的公网IP地址。如果服务器在NAT后需填写公网IP。 pasv_address你的服务器公网IP # 设置被动模式使用的端口范围方便防火墙放行 pasv_min_port30000 pasv_max_port30010注意pasv_address的配置是内网服务器通过NAT对外提供FTP服务的常见坑点。如果服务器直接拥有公网IP可以注释掉这行或设置为空。如果服务器在内网且路由器做了端口转发将21端口和30000-30010端口都转发到服务器内网IP那么这里就必须填写路由器的公网IP。步骤3设置防火墙如果系统防火墙如firewalld开启需要放行端口sudo firewall-cmd --permanent --add-port21/tcp sudo firewall-cmd --permanent --add-port30000-30010/tcp sudo firewall-cmd --reload步骤4启动服务并设置开机自启sudo systemctl start vsftpd sudo systemctl enable vsftpd步骤5创建FTP用户不建议直接使用root用户。创建一个专用用户并限制其登录Shell使其不能通过SSH登录系统只能用于FTPsudo useradd -m -s /sbin/nologin ftpuser sudo passwd ftpuser # 为用户设置密码用户的家目录如/home/ftpuser就是其登录FTP后的根目录。5.2 Windows客户端连接与常见问题排查在Windows上我们可以使用免费的FileZilla Client。连接步骤打开FileZilla在顶部快速连接栏输入主机服务器的公网IP地址或域名用户名ftpuser密码你设置的密码端口21默认点击“快速连接”。连接成功但列表失败/传输失败这十有八九是被动模式的问题。按照以下步骤排查检查FileZilla传输模式在FileZilla菜单栏选择“编辑” - “设置” - “连接” - “FTP”。确保“传输模式”选择的是“被动PASV”。这是默认设置通常不需要改。检查服务器防火墙确认服务器上30000-30010端口是否已正确放行见上文步骤3。检查网络设备如果服务器在云上检查云服务商的安全组规则是否放行了21和30000-30010端口。如果服务器在本地机房检查路由器或硬件防火墙的端口转发规则是否正确。查看FileZilla日志FileZilla下方有“状态”和“服务器回复”标签。连接时如果看到服务器返回类似227 Entering Passive Mode (192,168,1,10,117,44)的消息说明服务器进入了被动模式。括号里的数字前四个是IP后两个计算端口117*2564430036。如果客户端无法连接到这个IP:端口就会报错。这时就要重点检查这个IP是否是客户端能访问到的公网IP以及端口是否畅通。一个典型错误日志分析状态 正在连接 203.0.113.10:21... 状态 连接建立等待欢迎消息... 状态 不安全的服务器不支持 FTP over TLS。 状态 已登录 状态 读取目录列表... 命令 PASV 响应 227 Entering Passive Mode (10,0,0,2,117,44). # 注意这里返回的是内网IP 10.0.0.2 状态 服务器发回了不可路由的地址。使用服务器地址代替。 命令 LIST 错误 连接超时 错误 读取目录列表失败这个错误明确指出了问题服务器在被动模式响应中返回了它的内网IP地址10.0.0.2客户端当然无法直接连接这个地址。解决方法就是在服务器的vsftpd.conf中正确设置pasv_address为服务器的公网IP。6. 进阶话题FTP在现代技术栈中的位置即便我们讨论的是这个“古老”的协议它也能和现代技术栈发生关联。与容器化技术结合正如热词中提到的docker部署ftp服务器我们可以用Docker快速创建一个隔离的FTP服务环境用于开发测试。一个简单的docker-compose.yml示例如下version: 3 services: vsftpd: image: fauria/vsftpd container_name: my_ftp_server environment: - FTP_USERmyuser - FTP_PASSmypassword - PASV_ADDRESS127.0.0.1 # 宿主机IP在复杂网络下可能需要调整 - PASV_MIN_PORT30000 - PASV_MAX_PORT30010 ports: - 21:21 - 30000-30010:30000-30010 # 映射被动端口范围 volumes: - ./ftp_data:/home/vsftpd/myuser # 将本地目录挂载为用户目录这种方式一键部署干净利落不用在宿主机上安装和配置任何软件。在自动化脚本中的应用虽然对于自动化更推荐使用SCP或SFTP通过sshpass或密钥但某些老旧环境可能仍需FTP。在Linux Shell中可以使用lftp这个强大的命令行FTP客户端进行脚本化操作#!/bin/bash HOSTftp.example.com USERusername PASSpassword lftp -e set ftp:passive-mode on; mirror -R --verbose ./local_folder /remote_folder; quit -u $USER,$PASS $HOST这个脚本会将本地./local_folder目录下的内容递归地上传到服务器的/remote_folder目录。set ftp:passive-mode on是确保使用被动模式的关键。替代方案的选择对于全新的项目文件传输需求可以这样选型系统间自动化传输首选SFTP基于SSH或使用Rsync over SSH支持增量同步效率更高。Web应用文件上传通过应用本身实现如使用Multer for Node.js Django的FileField for Python存储到对象存储如AWS S3 阿里云OSS 腾讯云COS是更云原生、更 scalable 的方案。大文件分发考虑使用HTTP/HTTPS 分块下载、BitTorrent协议或专业的CDN服务。内网共享SMB/CIFSWindows文件共享或NFSLinux网络文件系统可能比FTP更贴近操作系统使用体验更好。FTP就像一把螺丝刀在电动工具普及的今天它可能不是最高效、最强大的选择但当你遇到一颗特定的螺丝或者需要在没有电源的地方工作时知道如何使用它并且了解它的局限比如不能当锤子用依然是一项有价值的基础技能。理解它是为了在合适的场景使用它或者在更好的替代方案出现时明白好在哪里。

相关新闻

怎样轻松解锁加密音乐:3步搞定QQ音乐.mflac格式转换

怎样轻松解锁加密音乐:3步搞定QQ音乐.mflac格式转换

怎样轻松解锁加密音乐:3步搞定QQ音乐.mflac格式转换 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: https:/…

2026/10/13 12:00:56 阅读更多 →
深度解析:如何用革命性AI工具一键将插画转化为专业分层结构

深度解析:如何用革命性AI工具一键将插画转化为专业分层结构

深度解析:如何用革命性AI工具一键将插画转化为专业分层结构 【免费下载链接】layerdivider A tool to divide a single illustration into a layered structure. 项目地址: https://gitcode.com/gh_mirrors/la/layerdivider 在数字艺术创作的世界中&#xff…

2026/10/13 12:00:48 阅读更多 →
JS逆向日志Day03——腾讯企业邮箱

JS逆向日志Day03——腾讯企业邮箱

1.目标:逆向分析“腾讯企业邮箱”密码加密 方式并在Pycharm进行复现2.加密位置分析先输入账号密码,打开F12,再发送登录请求通过载荷信息可见,账号是我们输入的账号,下面的密码“123456a”被进行了加密。载荷中密码的ke…

2026/10/13 12:00:42 阅读更多 →

最新新闻

TikTok Shop 多店铺:平台侧哪些信息会被交叉比对

TikTok Shop 多店铺:平台侧哪些信息会被交叉比对

一、先说结论:官方对「关联」的定义,比你想的宽 TikTok Shop 官方在卖家学习中心有一篇《关联账号风险指南》,原文写得很直白——平台对关联账号的判定,通常不是依据单一的重合信息,而是多项风险信号综合判断&#xff…

2026/10/14 3:24:35 阅读更多 →
小白程序员必看:FDE前沿部署工程师,AI落地新风口

小白程序员必看:FDE前沿部署工程师,AI落地新风口

本文介绍了FDE(前沿部署工程师)这一新兴AI岗位,主要职责是连接AI技术与企业实际业务,解决大模型落地难题。文章阐述了FDE的工作内容、所需技能(AI基础、RAG/Agent应用、项目实践、沟通能力),并分…

2026/10/14 3:24:35 阅读更多 →
我把一个只有 7 行的 AI Skill 用出了花:聊聊 eli5

我把一个只有 7 行的 AI Skill 用出了花:聊聊 eli5

一个小到离谱的"插件" 最近我在整理 AI 工具的时候,翻到一个特别不起眼的东西,叫 eli5。它的完整定义只有短短几行,翻译成人话就一句:假装我是一个对这个话题一无所知的人,用一张 HTML 页面、大图配少量文字…

2026/10/14 3:24:35 阅读更多 →
北美外教一对一哪个好?2026年最新深度横评,价格、口音、互动性三维度打分

北美外教一对一哪个好?2026年最新深度横评,价格、口音、互动性三维度打分

在线少儿北美外教一对一赛道,经过多年迭代,目前平台差异化已经非常明显。很多家长选课只看宣传噱头,忽略师资口音纯度、课堂互动落地性、长期性价比等核心指标,最终出现“高价低效、口音不标准、上课全程旁听”等问题。本文基于20…

2026/10/14 3:24:35 阅读更多 →
C++手搓坐标图工具:从坐标变换到实时渲染的工程实践

C++手搓坐标图工具:从坐标变换到实时渲染的工程实践

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

2026/10/14 3:24:34 阅读更多 →
Virglrenderer 虚拟化技术深入解析 · 专栏目录

Virglrenderer 虚拟化技术深入解析 · 专栏目录

一套面向工程师的 virglrenderer 源码级技术专栏。从概述与演进讲起,再深入核心抽象、各后端实现、部署集成与工程实践。所有内容以仓库源码为准。 这里讲一下我为什么关注 virglrenderer,因为国产 GPU 的虚拟化需求。 随着国产化替代的推进&#xff0c…

2026/10/14 3:23:34 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/12 18:11:17 阅读更多 →