Qt云盘开发核心是通信、线程与UI协同:QFileSystemModel仅支持本地目录,远程需自定义模型;上传下载须用线程隔离并信号回调;协议须分帧设计;SQLite多线程需独立连接与事务。

Qt 开发云盘文件管理系统,核心不是“能不能做”,而是“怎么组织通信、线程和 UI 更新才不崩”。直接上 QFileSystemModel 只能管本地浏览——真要对接服务器、支持上传下载、断点续传、好友分享,必须自己搭网络层和任务调度层。
用 QFileSystemModel 显示本地目录,但别指望它连服务器
QFileSystemModel 是 Qt 提供的本地文件系统模型,它只读取本机磁盘路径,不处理网络请求。你调 setRootPath("ftp://...") 或 "http://..." 会静默失败,不会报错,也不会加载任何内容。
- 正确用法是绑定到本地缓存目录,比如
setRootPath(QDir::homePath() + "/MyCloudCache") - 所有远程文件列表(如服务器上的“我的文件”)必须自己用
QStandardItemModel或自定义QAbstractItemModel实现 - 双击打开远程文件时,不能直接
QDesktopServices::openUrl(),得先触发下载逻辑,再用本地路径打开
文件上传下载必须脱离主线程,否则界面卡死
Qt 的 GUI 必须在主线程更新,但 QTcpSocket 的 write() 和 readAll() 在大文件场景下会阻塞,尤其遇到网络抖动或慢速上传时。直接在主线程里循环读写,QTreeView 就会冻结几秒甚至十几秒。
- 每个上传/下载任务必须用
QThread或QThreadPool+QRunnable隔离 - 工作线程里不能操作任何
QWidget或QAbstractItemModel,只能通过信号(emit progress(int))把进度、完成状态、错误信息发回主线程 - 不要用
moveToThread()把QTcpSocket移过去——它依赖于所属线程的事件循环,而QThread默认没有运行事件循环;要用QObject::moveToThread()+QThread::start()+ 手动exec(),或改用QThreadPool配合QEventLoop局部等待
协议设计决定扩展性,别用裸 JSON 拼接二进制文件
很多初学者用 QJsonDocument 包裹文件名、大小、MD5,再把文件体追在后面发,结果服务器解析错位、客户端收包不全、断点续传无法对齐。这不是 Qt 的问题,是协议没分帧。
- 必须定义固定头部:比如前 4 字节是包总长(
quint32网络序),后 1 字节是命令类型(UPLOAD_REQ=0x01,UPLOAD_DATA=0x02),再后 4 字节是偏移量(用于断点续传) - 文件数据块必须独立分包,每包 ≤64KB,避免单包过大触发 TCP 重传放大
- 上传时服务端返回的是 chunk ID 或 offset ACK,不是“成功”字符串;客户端靠这个判断是否重发,而不是靠超时重试
SQLite 存本地元数据,但别锁表太久
用户登录态、好友列表、已下载文件的本地路径和服务器 hash,都该存 SQLite。但如果你在上传回调里直接 INSERT INTO file_cache,又在另一个线程里 SELECT * FROM file_cache WHERE status='pending',大概率遇到数据库 busy 错误。
- 用
QSqlDatabase::addDatabase("QSQLITE", "cache_conn")显式命名连接,确保多线程各自用独立连接实例 - 所有写操作加事务:
transaction()+commit(),避免单条INSERT触发隐式锁 - 查询频率高的字段(如
server_id)建索引,但别给BLOB字段建索引
真正难的不是实现某个功能,而是让 TCP 连接断开时上传任务能暂停并恢复、让多线程同时操作同一份文件列表时不崩溃、让用户拖拽 100 个文件进窗口时 UI 依然响应——这些细节藏在 mytcpsocket.cpp 的重连逻辑、sharedfilefriendlist.cpp 的互斥锁粒度、以及 filesystem.cpp 对 QMetaObject::invokeMethod() 的谨慎调用里。











