#ifndef FILEDOWNLOADER_H
#define FILEDOWNLOADER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QFile>
#include <QCryptographicHash>
#include <QNetworkReply>
class FileDownloader : public QObject {
Q_OBJECT
public:
explicit FileDownloader(const QUrl& url, const QString& outputDir, const QString& fileExtension, QObject* parent = nullptr);
void download();
//void stopDownload(); // 停止下载接口
signals:
void downloadFinished(bool success, const QString& filePath);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private slots:
void startDownload();
void onReadyRead();
void onFinished();
void onErrorOccurred(QNetworkReply::NetworkError code);
//void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
void retryDownload();
QString calculateMD5(const QString& filePath);
void openFileLocation(const QString& filePath);
QUrl url;
QString outputDir;
QString fileExtension; // 指定的文件后缀
int retryCount;
const int maxRetries;
QNetworkAccessManager networkManager;
QScopedPointer<QFile> file;
QNetworkReply* reply;
QString tempFilePath;
};
#endif // FILEDOWNLOADER_H
#include "FileDownloader.h"
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
#include <QDebug>
#include <QFileInfo>
#include <QDesktopServices>
#include <QUrl>
#include <QDir>
#include <QProcess>
#include <QFile>
#include <QDebug>
FileDownloader::FileDownloader(const QUrl& url, const QString& outputDir, const QString& fileExtension, QObject* parent)
: QObject(parent), url(url), outputDir(outputDir), fileExtension(fileExtension), retryCount(0), maxRetries(5), reply(nullptr) {
connect(&networkManager, &QNetworkAccessManager::finished, this, &FileDownloader::onFinished);
tempFilePath = "temp_download_file"; // 临时文件名
}
void FileDownloader::download() {
qDebug() << "Starting download:" << url;
startDownload();
}
void FileDownloader::startDownload() {
if (reply) {
// 确保任何现有的回复在开始新下载之前得到处理
reply->abort();
reply->deleteLater();
reply = nullptr;
}
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "QtFileDownloader 1.0");
reply = networkManager.get(request);
connect(reply, &QNetworkReply::readyRead, this, &FileDownloader::onReadyRead);
connect(reply, &QNetworkReply::finished, this, &FileDownloader::onFinished);
connect(reply, &QNetworkReply::errorOccurred, this, &FileDownloader::onErrorOccurred);
connect(reply, &QNetworkReply::downloadProgress, this, &FileDownloader::downloadProgress);
file.reset(new QFile(tempFilePath));
if (!file->open(QIODevice::WriteOnly)) {
qWarning() << "Could not open file for writing:" << tempFilePath;
retryDownload();
}
}
void FileDownloader::onReadyRead() {
if (file && reply) {
file->write(reply->readAll());
}
}
bool deleteFile(const QString& filePath) {
QFile file(filePath);
if (file.exists()) {
if (file.remove()) {
qDebug() << "File deleted successfully:" << filePath;
return true;
}
else {
qWarning() << "Failed to delete file:" << filePath;
return false;
}
}
else {
qWarning() << "File does not exist:" << filePath;
return false;
}
}
void FileDownloader::onFinished() {
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
qDebug() << "Download finished successfully.";
file->flush();
file->close();
QString md5Hash = calculateMD5(tempFilePath);
QString newDirPath = QDir::current().filePath(outputDir); // 使用指定输出目录
QString newFilePath = QDir(newDirPath).filePath(md5Hash + fileExtension);
QDir dir(newDirPath);
if (!dir.exists()) {
dir.mkpath(newDirPath); // 创建目录
}
//DeleteFile()
deleteFile(newFilePath);
if (QFile::rename(tempFilePath, newFilePath)) {
qDebug() << "File moved to:" << newFilePath;
emit downloadFinished(true, newFilePath);
openFileLocation(newFilePath);
}
else {
qWarning() << "Failed to move file to destination directory.";
}
}
else {
retryDownload();
}
reply->deleteLater();
reply = nullptr;
}
}
void FileDownloader::onErrorOccurred(QNetworkReply::NetworkError code) {
qWarning() << "Network error occurred:" << code;
retryDownload();
}
void FileDownloader::retryDownload() {
if (reply) {
reply->abort();
reply->deleteLater();
reply = nullptr;
}
file.reset();
if (retryCount < maxRetries) {
retryCount++;
qDebug() << "Retrying... Attempt" << retryCount;
QTimer::singleShot(2000, this, &FileDownloader::startDownload); // 2秒后重试
}
else {
qWarning() << "Max retries reached. Download failed.";
emit downloadFinished(false, QString());
}
}
QString FileDownloader::calculateMD5(const QString& filePath) {
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Failed to open file for MD5 calculation:" << filePath;
return QString();
}
QCryptographicHash hash(QCryptographicHash::Md5);
if (hash.addData(&file)) {
return hash.result().toHex();
}
return QString();
}
void FileDownloader::openFileLocation(const QString& filePath) {
QFileInfo fileInfo(filePath);
#ifdef Q_OS_WIN
QString explorer = "explorer.exe";
QStringList args;
args << "/select," << QDir::toNativeSeparators(fileInfo.absoluteFilePath());
QProcess::startDetached(explorer, args);
#elif defined(Q_OS_MAC)
QStringList args;
args << "-e";
args << QString("tell application \"Finder\" to reveal POSIX file \"%1\"").arg(fileInfo.absoluteFilePath());
QProcess::execute("/usr/bin/osascript", args);
#elif defined(Q_OS_LINUX)
// For Linux, we try a variety of common file managers
QProcess::startDetached("xdg-open", QStringList() << fileInfo.absolutePath());
#else
// Fallback using the original method if not handled above
QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absolutePath()));
#endif
}
//调用测试
QUrl url("https://www.ffmpeg.org/releases/ffmpeg-5.1.3.tar.xz"); // Replace with your URL
QString outputDir = "12345678";//指定下载到哪个文件夹
FileDownloader downloader(url,outputDir,".dat");//指定下载文件后缀为.dat 文件名称以md5 命名
QObject::connect(&downloader, &FileDownloader::downloadFinished, [&](bool success,const QString&fileName) {
if (success) {
qDebug() << "Download completed successfully. Saved as:" << fileName;
} else {
qDebug() << "Download failed.";
}
QCoreApplication::quit();
});
downloader.download();
//tls 初始化错误 参考
Qt TLS initialization failed 问题解决-CSDN博客
openssl 的几个dll拷贝到运行目录就可以了