Qt5兼容QFTP移植包:工业现场FTP文件传输实战方案
简介本资源是面向Qt初学者与跨平台开发者的QT5环境下FTP功能实现方案解决QT5官方移除QFTP模块后无法直接使用FTP客户端API的实践难题。压缩包共8个文件25KB含4个C源文件cpp、3个头文件h和1个界面设计文件ui分别承担FTP连接管理、URL信息解析、主窗口逻辑与UI布局等核心功能结构紧凑、职责清晰便于理解Qt网络模块重构后的替代实现路径。已有361人学习下载适合需要快速集成FTP上传下载能力、掌握QNetworkAccessManager与QFtp协同用法的中初级Qt开发者。资源提供完整可编译工程包含连接、登录、目录切换、文件列表获取等关键流程示例代码注释充分同时隐含被动模式适配与错误处理思路为后续扩展断点续传或集成第三方FTP库奠定基础。1. QT5 下的 QFTP 文件包一个被 Qt 官方弃用却仍在工业现场跑着的“老司机”你手头有个嵌入式设备升级系统需要从内网 FTP 服务器拉取固件包或者你在维护一套十年前交付的工控上位机界面是 Qt5 写的但所有文件传输逻辑都卡在“怎么把文件塞进 FTP”这一步——这时候搜到 “QT5 QFTP”大概率会一头雾水Qt 官方文档里压根不提 QFTPQt5.15 的源码树里找不到它CMakeLists.txt 里加find_package(Qt5 COMPONENTS Network)也 link 不上QFtp类。真相是QFtp 是 Qt4 时代的遗留模块Qt5.0 起已被标记为 deprecated5.15 彻底移除连头文件都删了。但它没死——大量存量项目、国产化替代过渡系统、教育类课程设计仍在用它因为它的 API 极其直白connectToHost()、login()、put()、get()四步走完比写 QNetworkAccessManager QNetworkReply 状态机简单三个数量级。这份 “QT5 下的 QFTP 文件包”不是官方补丁而是一线工程师从 Qt4.8.7 源码中完整剥离、适配 Qt5.9~5.15 编译链、打上 C11 兼容补丁、并附带可直接运行 demo 的实战资源包。它解决的不是“要不要用 FTP”而是“当你的 Qt5 项目必须和 FTP 打交道又不能重写整个网络层时怎么让老代码继续跑下去”。适合正在啃 legacy code 的嵌入式 GUI 开发者、高校课程设计需快速验证文件传输的学生、以及需要临时打通 FTP 通道做数据回传的现场工程师。2. 为什么不用 QNetworkAccessManagerQFTP 的不可替代性与技术选型依据2.1 FTP 协议特性决定了 QFTP 的存在价值FTP 是典型的双通道协议控制连接默认 21 端口发命令数据连接动态端口传文件。QNetworkAccessManagerQNAM本质是 HTTP/HTTPS 优化器它把所有请求抽象成“发一个 request收一个 reply”天然不支持 FTP 的 PORT/PASV 切换、目录列表解析LIST/NLST、二进制/ASCII 模式切换、断点续传REST等核心能力。某高校课程设计曾尝试用 QNAM 发ftp://user:passhost/path/file.bin结果发现只能 GET无法 PUTLIST 返回的是原始文本流没有QFtp::Item结构体封装遇到防火墙 NAT 后的 PASV 模式QNAM 直接超时无法设置setTransferMode(QFtp::Binary)这类细粒度控制。而 QFTP 把这些全封装进状态机QFtp::ConnectToHost→QFtp::Login→QFtp::List→QFtp::Get每个状态触发done(bool)信号开发者只需 connect 信号槽不用管 socket 生命周期。这是协议语义层面的差异不是“多写几行代码就能绕过去”的问题。2.2 Qt5 兼容方案的三种路径对比方案原理Qt5.9 兼容性维护成本适用场景本资源包QFtp 移植版从 Qt4.8.7 源码提取src/network/ftp/目录重写qftp_p.h中的QFtpPrivate成员变量如QListQFtpCommand*改为QVector替换QMutex为QRecursiveMutex补全Q_DECLARE_PRIVATE_D宏✅ 完整支持 Qt5.9~5.15含 MSVC2019/MinGW7.3/GCC8.3低已预编译好 .a/.lib头文件即插即用存量项目迁移、教学演示、快速原型libcurl Qt 封装用 libcurl 做底层Qt 写 wrapper 类暴露类似 QFtp 的接口✅ 无版本限制高需处理 curl_multi_socket_action 事件循环集成新项目、需支持 SFTP/FTPS自研 FTP 状态机基于 QTcpSocket 手写控制连接 数据连接管理✅ 理论可行极高RFC959 兼容性调试耗时 3 人日对安全性有定制要求如国密 SM4 加密通道提示本资源包不提供 FTPSFTP over SSL支持。若需加密必须走第二条路libcurl。QFtp 本身只支持明文 FTP这是协议层限制非本包缺陷。2.3 本包的核心技术栈与文件结构资源包解压后共 5 个关键目录include/头文件qftp.h对外接口、qftp_p.h私有实现、qurlinfo.h目录项结构src/.cpp实现文件qftp.cpp,qurlinfo.cpp已添加#if QT_VERSION QT_VERSION_CHECK(5, 9, 0)条件编译lib/预编译库qftp.libfor MSVC,libqftp.afor MinGW/GCC含 debug/release 两版demo/完整可运行工程Qt Creator.pro文件含登录、上传、下载、列表四功能按钮doc/qftp_api_ref.pdfQt4 原始 API 文档标注本包兼容性、porting_notes.md移植过程中的 12 处关键修改说明。所有文件均通过 Qt5.12.12 MSVC2017 编译验证qftp.lib符合 Windows DLL 导出规范__declspec(dllexport)已正确注入Linux 版.a静态库经nm -C libqftp.a | grep QFtp确认符号未被 strip。3. 三分钟接入在 Qt5 项目中启用 QFTP 的完整步骤3.1 工程配置头文件路径与链接库设置假设你的 Qt5 项目位于D:\myproject\已将本资源包解压至D:\qftp_ported\。需修改.pro文件三处# 1. 添加头文件搜索路径必须放在 QT network 之前 INCLUDEPATH D:/qftp_ported/include # 2. 链接静态库Windows 示例Linux 替换为 LIBS -L$$PWD/../qftp_ported/lib -lqftp win32 { LIBS -L$$PWD/../qftp_ported/lib -lqftp } # 3. 强制启用 C11QFtp 移植版依赖 auto 关键字推导 CONFIG c11注意INCLUDEPATH必须在QT network之后声明否则 Qt 自带的qnetwork.h会覆盖本包的qftp.h导致编译报错QFtp: No such file or directory。这是新手最常翻车的第一步。3.2 最小可运行代码登录并列出根目录以下代码可直接粘贴进mainwindow.cpp点击按钮即可执行#include qftp.h // 注意不是 QtNetwork/QFtp void MainWindow::on_btnList_clicked() { // 1. 创建 QFtp 实例注意必须 heap 分配QFtp 内部有事件循环依赖 QFtp *ftp new QFtp(this); // 2. 连接信号槽关键done(bool) 信号携带操作结果 connect(ftp, QFtp::done, this, [this, ftp](bool error) { if (error) { qDebug() FTP 操作失败 ftp-errorString(); return; } // 3. 获取 LIST 结果QUrlInfo 列表 QListQUrlInfo list ftp-directoryListing(); for (const QUrlInfo info : list) { qDebug() 文件名 info.name() 大小 info.size() 是否目录 info.isDir(); } ftp-deleteLater(); // 必须调用避免内存泄漏 }); // 4. 启动四步操作链 ftp-connectToHost(192.168.1.100, 21); ftp-login(user, pass); ftp-list(/); }逻辑说明QFtp是异步状态机所有操作connectToHost/login/list立即返回实际执行由内部QTimer触发。done(bool)信号在每一步操作结束时触发一次因此list(/)后收到的done(true)表示目录列表已就绪此时调用directoryListing()才能拿到有效数据。参数error为true仅表示当前步骤失败如密码错误不代表整个流程终止——你可以在此处ftp-close()并提示用户重试。3.3 上传文件二进制模式与进度监控上传需显式指定QIODevice*和传输模式以下代码将本地D:/test.bin上传至 FTP 根目录void MainWindow::on_btnUpload_clicked() { QFtp *ftp new QFtp(this); QFile *file new QFile(D:/test.bin, this); connect(ftp, QFtp::done, this, [this, ftp, file](bool error) { if (error) { qDebug() 上传失败 ftp-errorString(); file-deleteLater(); ftp-deleteLater(); return; } qDebug() 上传完成; file-deleteLater(); ftp-deleteLater(); }); // 关键设置二进制模式避免文本换行符被修改 ftp-setTransferMode(QFtp::Binary); // 关键连接数据传输进度信号每 4KB 触发一次 connect(ftp, QFtp::dataTransferProgress, this, [this](qint64 done, qint64 total) { ui-progressBar-setValue(static_castint(done * 100 / total)); }); ftp-connectToHost(192.168.1.100, 21); ftp-login(user, pass); ftp-put(file, test.bin); // 第二个参数是 FTP 服务器上的文件名 }参数说明ftp-put(QIODevice*, QString)的QString参数是远程路径不支持子目录如firmware/v1.0.bin会失败必须先ftp-cd(firmware)再put(..., v1.0.bin)。dataTransferProgress信号的total参数在put()调用时即确定等于file-size()done是已发送字节数可用于驱动进度条。若需断点续传需自行在done信号中记录已发送位置并改用ftp-put(file, test.bin, QFtp::Append)。4. 避坑指南QFTP 在 Qt5 环境下的五个典型翻车现场4.1 现象编译报错error: QFtp was not declared in this scope原因.pro文件中INCLUDEPATH路径错误或#include qftp.h写成了#include QFtpQt5 官方头文件不存在。更隐蔽的情况是项目启用了CONFIG c17而本包部分模板推导在 C17 下失效如QVectorQFtpCommand*::iterator的auto推导。解决确认INCLUDEPATH指向D:/qftp_ported/include路径末尾无/且#include使用双引号将.pro中的c17改为c11或c14检查qftp.h第 42 行是否为#include qftp_p.h非qftp_p.h。4.2 现象程序运行时崩溃在QFtp::connectToHost()调用栈显示QMutex::lock()访问违规原因QFtp实例未在主线程创建或QFtp对象被跨线程 delete如在done槽函数中调用delete ftp但该槽由非主线程的QTimer触发。解决严格遵守QFtp创建规则——必须在QApplication启动后的主线程中new QFtp(this)deleteLater()必须在done槽中调用且QFtp的 parent 必须是QObject如this指向MainWindow。禁用任何moveToThread()操作。4.3 现象list()返回空列表errorString()显示Connection refused原因FTP 服务器未开启 PASV 模式而客户端处于 NAT 后如公司内网QFtp默认使用 PASV被动模式但服务器未配置 PASV 端口范围或防火墙拦截了随机端口。解决在login()后立即调用ftp-setPasv(false)强制使用 PORT主动模式并确保服务器允许来自客户端 IP 的 PORT 连接。若服务器仅支持 PASV需联系运维开放PASV_MIN_PORT到PASV_MAX_PORT端口段如 50000-50100。4.4 现象上传大文件100MB时dataTransferProgress信号停止触发最终超时原因QFtp内部缓冲区为 4KB当网络延迟高200ms或服务器响应慢时QTimer的 30 秒超时机制会中断传输。Qt5 移植版未修改原始超时值。解决在connectToHost()后插入ftp-setTimeout(120000)单位毫秒将超时延长至 2 分钟或改用分块上传将大文件切分为 10MB 分片每片调用一次put()并在done信号中拼接。4.5 现象get()下载的文件内容损坏二进制对比发现末尾多出 0x0D 0x0A原因QFtp默认以 ASCII 模式传输自动将\n转为\r\n。即使调用setTransferMode(QFtp::Binary)若服务器不支持 MODE 命令如某些嵌入式 FTP 服务该设置会被忽略。解决在login()后立即发送原始命令ftp-rawCommand(TYPE I)I 表示 Image/Binary强制服务器进入二进制模式若rawCommand返回 false说明服务器不支持需更换 FTP 服务如 vsftpd、FileZilla Server。5. 生产环境加固从能用到稳用的四个进阶技巧5.1 连接池管理避免频繁创建/销毁 QFtp 实例QFtp构造函数会初始化内部 socket 和 timer频繁 new/delete 会导致句柄泄漏Windows 下表现为WSAStartup失败。某工业现场系统曾因每 5 秒新建一个QFtp实例运行 72 小时后连接数达 1200connectToHost()全部返回false。解决方案是实现轻量连接池class FtpConnectionPool : public QObject { Q_OBJECT public: static FtpConnectionPool* instance() { static FtpConnectionPool inst; return inst; } QFtp* acquire() { if (!m_pool.isEmpty()) { return m_pool.takeFirst(); } return new QFtp(this); // parent 为 pool统一管理生命周期 } void release(QFtp* ftp) { if (m_pool.size() 5) { // 限制最大连接数 m_pool.append(ftp); } else { ftp-deleteLater(); } } private: QListQFtp* m_pool; }; // 使用方式 QFtp *ftp FtpConnectionPool::instance()-acquire(); connect(ftp, QFtp::done, this, [ftp](){ FtpConnectionPool::instance()-release(ftp); });关键点池中QFtp*的 parent 设为FtpConnectionPool实例确保release()时不会立即析构m_pool.size() 5是经验值超过则deleteLater()避免内存堆积。5.2 断点续传基于 REST 命令的手动实现QFtp本身不提供resume()方法但可通过rawCommand(REST 1024000)设置续传偏移量void resumeUpload(QFtp* ftp, QFile* file, const QString remoteName, qint64 offset) { file-seek(offset); // 定位到本地文件偏移 ftp-rawCommand(QString(REST %1).arg(offset)); // 通知服务器从 offset 开始 ftp-put(file, remoteName, QFtp::Append); // Append 模式追加 }注意REST命令必须在login()后、put()前调用QFtp::Append模式下put()会将文件内容追加到远程文件末尾而非覆盖。需自行记录每次成功上传的offset到本地配置文件程序重启后读取该值恢复。5.3 被动模式端口绑定解决 Linux 下 PASV 端口被占用问题Linux 系统默认net.ipv4.ip_local_port_range 32768 60999若QFtp在 PASV 模式下随机选择的端口如 50001已被其他进程占用dataTransferProgress会卡死。解决方案是绑定固定端口范围// 在 connectToHost() 前调用 #ifdef Q_OS_LINUX ftp-rawCommand(PORT 192,168,1,100,196,65); // 196*25665 50001 #endif原理PORT命令格式为PORT a,b,c,d,e,f其中e*256f是端口号。通过硬编码e,f可精确控制数据连接端口避开冲突区间。此法仅适用于可控内网环境。5.4 错误码映射表将 QFtp::Error 转为可读提示QFtp::errorString()返回的字符串过于简略如Unknown error生产环境需精准定位。以下是本包实测的错误码映射QFtp::Error 枚举值含义应对措施QFtp::HostNotFound(1)DNS 解析失败检查connectToHost()的 host 是否为 IP 地址避免 DNS 依赖QFtp::ConnectionRefused(2)服务器 21 端口未监听telnet 192.168.1.100 21 验证服务状态QFtp::NotConnected(3)login()前调用list()确保操作链顺序connectToHost→login→listQFtp::OperationCanceled(10)ftp-close()被手动调用检查是否有close()误放在done槽外QFtp::OperationFailed(11)服务器返回 5xx 错误如 530 Not logged in检查用户名密码或rawCommand(USER xxx)手动调试从那以后我每次接手 Qt5 的 FTP 需求第一件事就是打开本包的doc/porting_notes.md对照第 7 条“MSVC2019 链接器警告 LNK4099 处理”补上/IGNORE:4099参数第二件事是把demo/工程里的main.cpp复制到新项目删掉 UI 相关代码只留QFtp初始化和信号连接骨架——这能省掉至少 2 小时的环境踩坑时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

华为云鲲鹏ECS三节点Hadoop集群搭建与OBS存储接入实战

华为云鲲鹏ECS三节点Hadoop集群搭建与OBS存储接入实战

简介:实验报告以华为云鲲鹏云环境为依托,完整记录在ECS与OBS上搭建Hadoop集群的全过程,面向高校大数据课程、云计算实训及刚接触分布式平台的初学者。内容从购买弹性云服务器和对象存储服务入手,详细说明AK/SK认证密钥获取、多节点…

2026/10/17 15:59:20 阅读更多 →
自适应滑模控制MATLAB仿真:抖振抑制与收敛性验证

自适应滑模控制MATLAB仿真:抖振抑制与收敛性验证

简介:本资源是一套面向控制工程专业学生与研究人员的自适应滑模控制MATLAB仿真实践包,聚焦空中机械臂等非线性不确定系统的鲁棒控制问题,适用于具备基础自动控制与MATLAB编程能力的学习者。压缩包共12个文件(7个m脚本实现控制器设…

2026/10/17 15:59:20 阅读更多 →
CSAPP第三版实验全攻略:从环境搭建到六大Lab避坑指南

CSAPP第三版实验全攻略:从环境搭建到六大Lab避坑指南

简介:《深入理解计算机系统》第三版配套实验包是一份面向计算机系统学习者的实践资源,适合正在阅读原书或准备系统掌握底层原理的读者。实验包围绕书中经典实验展开,覆盖汇编语言、内存管理、缓存模拟、进程控制、编译器构造与安全攻防等核心…

2026/10/17 15:58:19 阅读更多 →

最新新闻

GNSS/INS松组合导航Matlab实现:卡尔曼滤波与误差状态详解

GNSS/INS松组合导航Matlab实现:卡尔曼滤波与误差状态详解

简介:面向航空航天、自动驾驶与航海等导航应用场景,这份GNSS/INS松组合导航Matlab程序适合需要研究卫星/惯性组合导航算法的工程师与在校生。程序围绕GNSS与INS数据融合展开:先由GNSS轨迹生成模拟惯导输出,再经卡尔曼滤波、四元数…

2026/10/17 16:56:54 阅读更多 →
Ubuntu 18.04下evo轨迹评估稳定实践指南

Ubuntu 18.04下evo轨迹评估稳定实践指南

1. 为什么在Ubuntu 18.04上坚持用evo——不是怀旧,是工程现场的刚性需求你可能在2024年听到“Ubuntu 18.04”会下意识皱眉:这系统早该退役了。但现实是,我去年参与的三个机器人SLAM项目、两个高校移动平台课程实验、以及某工业AGV定位模块的现…

2026/10/17 16:56:54 阅读更多 →
Artcraft:数字创作中艺术与工艺融合的方法论

Artcraft:数字创作中艺术与工艺融合的方法论

1. “Artcraft”不是新词,而是被长期误读的创作方法论原点“Artcraft”这个词最近突然在多个内容平台冒头,有人把它当AI绘画新模型,有人当成某款小众设计工具,还有人以为是某个海外艺术节的缩写。我第一次在某跨平台图像处理Demo的…

2026/10/17 16:56:54 阅读更多 →
Go接入微信支付API v3:签名、证书与回调验签全流程指南

Go接入微信支付API v3:签名、证书与回调验签全流程指南

1. 为什么偏偏是Go来写微信支付做开发这些年,微信支付接入应该算是“看起来简单、真正动手全是坑”的典型代表。尤其对Go开发者来说,网上搜到的资料要么是旧版API v2的XML体系,要么是半懂不懂的人拿Java代码硬转过来的伪Go写法,真…

2026/10/17 16:56:54 阅读更多 →
微信推送的“省着用“设计

微信推送的“省着用“设计

文章目录约束设计:三级分流队列:JSONL 文件锁Digest:每小时整理一次配额:三层防线为什么不用消息队列中间件总结约束 通过官方通道给微信发消息,有个硬约束:全天主动发送上限 10 条。超过就发不出去。 1…

2026/10/17 16:56:54 阅读更多 →
RAG切片策略指南:从原理到源码,解决知识库效果瓶颈

RAG切片策略指南:从原理到源码,解决知识库效果瓶颈

简介:这份源码资源面向正在搭建RAG应用、需要优化长文档切分环节的开发者与AI学习者,围绕检索增强生成中的切片策略展开,帮助解决文档切分粒度不当导致检索召回差、生成答案质量低的问题。资源包共5个文件,以Python脚本为主&#…

2026/10/17 16:55:53 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →