#ifndef DOWNLOADER_H #define DOWNLOADER_H #include #include #include #include #include #include extern QString CHECK_URL; class Downloader : public QObject { Q_OBJECT public: /** * @brief 构造联网下载器。 * @param parent Qt 父对象,负责随父对象析构自动释放网络管理器。 * * 设计意图:Downloader 只负责单文件下载和本地落盘,不直接操作界面。 * 网络请求、QFile 都运行在创建它的线程中,外部跨线程调用时应使用信号槽队列连接, * 避免在其它线程直接读写 QFile 或 QNetworkReply。 */ explicit Downloader(QObject *parent = nullptr); /** * @brief 设置默认下载地址。 * @param url 输入的远程文件 URL;为空或非法时不会主动下载。 */ void setUrl(const QString &); /** * @brief 开始下载文件。 * @param url 可选下载地址;为空时使用 setUrl() 已保存的地址。 * * 输出通过 doProgress/doFinished/onError/doShowInfo 信号返回。 * 边界条件:同一 Downloader 同时只允许一个请求;目标文件名为空或 URL 非法会直接报错。 * 资源风险:下载过程写入 .part 临时文件,成功后替换为目标文件名;异常时关闭文件并清理网络对象。 */ void startDownload(const QString &url = QString()); /** * @brief 触发版本检测请求。 * * 当前接口保留兼容旧调用;版本检测结果由 QNetworkAccessManager::finished 处理。 */ void checkVersion(); /** * @brief 获取当前落盘文件对象。 * @return 下载完成后的 QFile 引用;调用方只应读取文件名/路径,不应在下载中修改其打开状态。 */ const QFile &getFile() const; /** * @brief 获取下载目录。 * @return 应用程序目录;用于外部定位下载包。 */ const QDir &getDir() const; /** * @brief 获取目标文件名。 * @return 不包含目录的本地文件名。 */ const QString &fileName() const; /** * @brief 设置目标文件名。 * @param newFileName 不包含路径的文件名,避免远程响应头异常覆盖任意目录。 */ void setFileName(const QString &newFileName); public slots: signals: /** @brief 下载进度,received/total 单位为字节;total 为 -1 时表示服务器未给出总长度。 */ void doProgress(qint64, qint64); /** @brief 网络错误通知;错误发生后 Downloader 会关闭文件并释放 reply。 */ void onError(QNetworkReply::NetworkError); /** @brief 下载完成通知;仅在网络无错误且临时文件成功改名后发出。 */ void doFinished(); /** @brief 文本状态通知,用于把边界条件、文件错误等信息显示到界面。 */ void doShowInfo(const QString&); private: /** * @brief 关闭当前请求并释放 QNetworkReply。 * @param removePartial 为 true 时删除 .part 临时文件,常用于错误/取消场景。 * * 设计意图:统一网络对象和文件句柄的释放路径,避免 finished/error 分支重复清理导致二次关闭。 */ void cleanupCurrentReply(bool removePartial); /** * @brief 计算临时下载文件的完整路径。 * @return 当前目标文件名对应的 .part 路径;文件名为空时返回空字符串。 */ QString partialFilePath() const; /** * @brief 计算最终下载文件的完整路径。 * @return 当前目标文件名对应的最终路径;文件名为空时返回空字符串。 */ QString targetFilePath() const; /** @brief 网络访问管理器,归 Downloader 父子对象树管理,必须在同一线程使用。 */ QNetworkAccessManager *m_pNetWorkAccessManager{nullptr}; /** @brief 当前下载响应对象,finished 后通过 deleteLater 释放,避免悬空回调。 */ QNetworkReply *m_pReply{nullptr}; /** @brief 正在下载标识,防止重复点击造成多个请求同时写同一个文件。 */ bool m_bIsDownloading = false; /** @brief 当前下载 URL;startDownload 允许临时覆盖该地址。 */ QUrl m_pUrl{}; /** @brief 本地目标文件名,仅保存文件名不保存目录,避免路径注入。 */ QString m_fileName = ""; /** @brief 下载落盘文件句柄,下载中指向 .part 文件,完成后指向正式文件。 */ QFile file; /** @brief 下载目录,默认应用程序目录;目录不存在时 startDownload 会创建。 */ QDir dir; }; #endif // DOWNLOADER_H