2025-05-29 14:04:05 +08:00
|
|
|
|
#ifndef FILETRANSFER_H
|
|
|
|
|
|
#define FILETRANSFER_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
|
|
#include <QNetworkAccessManager>
|
|
|
|
|
|
#include <QNetworkReply>
|
|
|
|
|
|
#include <QFile>
|
|
|
|
|
|
#include <QDir>
|
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
#include <QHttpMultiPart>
|
|
|
|
|
|
|
|
|
|
|
|
class FileTransfer : public QObject {
|
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
Q_PROPERTY(double downloadProgress READ downloadProgress NOTIFY downloadProgressChanged)
|
|
|
|
|
|
Q_PROPERTY(double uploadProgress READ uploadProgress NOTIFY uploadProgressChanged)
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
static FileTransfer* instance() {
|
|
|
|
|
|
static FileTransfer _instance;
|
|
|
|
|
|
return &_instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Q_INVOKABLE void postDownload(const QString& url,
|
|
|
|
|
|
const QString& savePath,
|
|
|
|
|
|
const QVariantMap& postData,
|
|
|
|
|
|
const QString& additionalobj) {
|
|
|
|
|
|
if (m_currentDownload) {
|
|
|
|
|
|
emit downloadCompleted(false, u8"已有进行中的下载");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取文件所在的目录路径
|
|
|
|
|
|
QFileInfo fileInfo(savePath);
|
|
|
|
|
|
QString dirPath = fileInfo.absolutePath();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查目录是否存在,如果不存在则创建
|
|
|
|
|
|
QDir dir;
|
|
|
|
|
|
if (!dir.exists(dirPath)) {
|
|
|
|
|
|
if (!dir.mkpath(dirPath)) {
|
|
|
|
|
|
emit downloadCompleted(false, u8"无法创建目录");
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建目标文件
|
|
|
|
|
|
m_downloadFile = new QFile(savePath, this);
|
|
|
|
|
|
if (!m_downloadFile->open(QIODevice::WriteOnly)) {
|
|
|
|
|
|
emit downloadCompleted(false, u8"无法创建文件");
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QUrl u(url);
|
|
|
|
|
|
// 配置 HTTPS
|
|
|
|
|
|
QNetworkRequest request(u);
|
|
|
|
|
|
QSslConfiguration sslConfig = request.sslConfiguration();
|
|
|
|
|
|
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone); // 跳过证书验证
|
|
|
|
|
|
request.setSslConfiguration(sslConfig);
|
|
|
|
|
|
|
|
|
|
|
|
// 构建 POST 数据(JSON 格式示例)
|
|
|
|
|
|
QJsonDocument jsonDoc(QJsonObject::fromVariantMap (postData));
|
|
|
|
|
|
QByteArray postBody = jsonDoc.toJson();
|
|
|
|
|
|
|
|
|
|
|
|
// 发送请求
|
|
|
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
|
|
|
|
m_currentDownload = manager.post(request, postBody);
|
|
|
|
|
|
|
|
|
|
|
|
// 连接信号
|
|
|
|
|
|
connect(m_currentDownload, &QNetworkReply::readyRead, [this](){
|
|
|
|
|
|
m_downloadFile->write(m_currentDownload->readAll());
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
connect(m_currentDownload, &QNetworkReply::downloadProgress,
|
|
|
|
|
|
[this,additionalobj](qint64 bytesReceived, qint64 bytesTotal){
|
|
|
|
|
|
m_downloadProgress = bytesTotal > 0
|
|
|
|
|
|
? bytesReceived / (double)bytesTotal
|
|
|
|
|
|
: 0;
|
|
|
|
|
|
emit downloadProgressChanged(m_downloadProgress,additionalobj);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
connect(m_currentDownload, &QNetworkReply::finished, [this,additionalobj](){
|
|
|
|
|
|
if (m_downloadFile) {
|
|
|
|
|
|
m_downloadFile->flush();
|
|
|
|
|
|
m_downloadFile->close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 09:46:04 +08:00
|
|
|
|
QVariant statusCodeVariant = m_currentDownload->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
|
|
|
|
|
if (statusCodeVariant.isValid()) {
|
|
|
|
|
|
int statusCode = statusCodeVariant.toInt();
|
|
|
|
|
|
qDebug() << "HTTP Status Code:" << statusCode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-29 14:04:05 +08:00
|
|
|
|
bool success = !m_currentDownload->error();
|
|
|
|
|
|
QString message = success ? additionalobj : m_currentDownload->errorString();
|
|
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
// 验证文件完整性
|
|
|
|
|
|
QFileInfo fi(m_downloadFile->fileName());
|
|
|
|
|
|
qint64 expectedSize = m_currentDownload->header(
|
|
|
|
|
|
QNetworkRequest::ContentLengthHeader).toLongLong();
|
|
|
|
|
|
if (fi.size() != expectedSize && expectedSize > 0) {
|
|
|
|
|
|
success = false;
|
|
|
|
|
|
message = "文件不完整";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
emit downloadCompleted(success, message.toUtf8());
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Q_INVOKABLE void postUpload(const QString& url,
|
|
|
|
|
|
const QString& filePath,
|
|
|
|
|
|
const QString& remotePath,
|
|
|
|
|
|
const QString& additionalobj) {
|
|
|
|
|
|
if (m_currentDownload) { // 复用同一个网络管理器,保持单请求
|
|
|
|
|
|
emit uploadCompleted(false, u8"已有进行中的传输");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 准备要上传的文件
|
|
|
|
|
|
QFile *uploadFile = new QFile(filePath, this);
|
|
|
|
|
|
if (!uploadFile->open(QIODevice::ReadOnly)) {
|
|
|
|
|
|
emit uploadCompleted(false, u8"无法打开文件");
|
|
|
|
|
|
uploadFile->deleteLater();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建多部分数据
|
|
|
|
|
|
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加文件部分
|
|
|
|
|
|
QHttpPart filePart;
|
|
|
|
|
|
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
|
|
|
|
|
filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
|
|
|
|
|
QVariant(QString("form-data; name=\"file\"; filename=\"%1\"")
|
|
|
|
|
|
.arg(remotePath + QFileInfo(filePath).fileName())));
|
|
|
|
|
|
filePart.setBodyDevice(uploadFile);
|
|
|
|
|
|
uploadFile->setParent(multiPart); // 文件对象由multiPart管理生命周期
|
|
|
|
|
|
multiPart->append(filePart);
|
|
|
|
|
|
|
|
|
|
|
|
QUrl u(url);
|
|
|
|
|
|
QNetworkRequest request(u);
|
|
|
|
|
|
|
|
|
|
|
|
// 配置HTTPS
|
|
|
|
|
|
QSslConfiguration sslConfig = request.sslConfiguration();
|
|
|
|
|
|
sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
|
|
|
|
|
|
request.setSslConfiguration(sslConfig);
|
|
|
|
|
|
|
|
|
|
|
|
// 发送请求
|
|
|
|
|
|
m_currentDownload = manager.post(request, multiPart);
|
|
|
|
|
|
multiPart->setParent(m_currentDownload); // 由reply管理multiPart生命周期
|
|
|
|
|
|
|
|
|
|
|
|
// 连接进度信号
|
|
|
|
|
|
connect(m_currentDownload, &QNetworkReply::uploadProgress,
|
|
|
|
|
|
[this, additionalobj](qint64 bytesSent, qint64 bytesTotal){
|
|
|
|
|
|
m_uploadProgress = bytesTotal > 0
|
|
|
|
|
|
? bytesSent / (double)bytesTotal
|
|
|
|
|
|
: 1;
|
|
|
|
|
|
emit uploadProgressChanged(m_uploadProgress, additionalobj); // 带附加参数
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 处理完成信号
|
|
|
|
|
|
connect(m_currentDownload, &QNetworkReply::finished, [this, additionalobj](){
|
|
|
|
|
|
bool success = (m_currentDownload->error() == QNetworkReply::NoError);
|
|
|
|
|
|
QString message = success ? u8"上传成功" : m_currentDownload->errorString();
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
emit uploadCompleted(success, additionalobj);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double downloadProgress() const { return m_downloadProgress; }
|
|
|
|
|
|
double uploadProgress() const { return m_uploadProgress; }
|
|
|
|
|
|
|
|
|
|
|
|
signals:
|
|
|
|
|
|
// 下载相关信号
|
|
|
|
|
|
void downloadProgressChanged(double progress,const QString& additional);
|
|
|
|
|
|
void downloadCompleted(bool success, const QString& message);
|
|
|
|
|
|
|
|
|
|
|
|
// 上传相关信号(原有)
|
|
|
|
|
|
void uploadProgressChanged(double progress ,const QString& additional);
|
|
|
|
|
|
void uploadCompleted(bool success, const QString& message);
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
explicit FileTransfer(QObject* parent = nullptr)
|
|
|
|
|
|
: QObject(parent), m_downloadProgress(0), m_uploadProgress(0),
|
|
|
|
|
|
m_currentDownload(nullptr), m_downloadFile(nullptr) {}
|
|
|
|
|
|
|
|
|
|
|
|
QNetworkAccessManager manager;
|
|
|
|
|
|
double m_downloadProgress;
|
|
|
|
|
|
double m_uploadProgress;
|
|
|
|
|
|
QNetworkReply* m_currentDownload;
|
|
|
|
|
|
QFile* m_downloadFile;
|
|
|
|
|
|
|
|
|
|
|
|
void cleanup() {
|
|
|
|
|
|
if (m_downloadFile) {
|
|
|
|
|
|
m_downloadFile->deleteLater();
|
|
|
|
|
|
m_downloadFile = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (m_currentDownload) {
|
|
|
|
|
|
m_currentDownload->deleteLater();
|
|
|
|
|
|
m_currentDownload = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
m_downloadProgress = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
#endif // FILETRANSFER_H
|