|
|
|
@ -13,6 +13,88 @@ static const std::string baseUrl = "https://api.hammer-hfut.tk:233/aics/file/";
|
|
|
|
|
//static const std::string baseUrl = "http://127.0.0.1:4523/m1/2914957-0-6e5f2db1/";
|
|
|
|
|
//static const std::string baseUrl = "https://quic.nginx.org/";
|
|
|
|
|
|
|
|
|
|
struct FileDownloading : FileItem
|
|
|
|
|
{
|
|
|
|
|
CURL *curlHandle = nullptr;
|
|
|
|
|
std::ofstream file;
|
|
|
|
|
};
|
|
|
|
|
std::unordered_map<QString, FileDownloading> downloadingMap;
|
|
|
|
|
std::mutex downloadingMapMutex;
|
|
|
|
|
|
|
|
|
|
struct FileUploading : FileItem
|
|
|
|
|
{
|
|
|
|
|
CURL *curlHandle = nullptr;
|
|
|
|
|
std::ifstream file;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::unordered_map<QString, FileUploading> uploadingMap;
|
|
|
|
|
std::mutex uploadingMapMutex;
|
|
|
|
|
|
|
|
|
|
size_t writeDataFunc(const void *ptr, size_t size, size_t nmemb, void *obj)
|
|
|
|
|
{
|
|
|
|
|
auto buf = (FileDownloading *) obj;
|
|
|
|
|
auto writeSize = size * nmemb;
|
|
|
|
|
buf->file.write(reinterpret_cast<const char *>(ptr), writeSize);
|
|
|
|
|
return writeSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t readDataFunc(void *ptr, size_t size, size_t nmemb, void *obj)
|
|
|
|
|
{
|
|
|
|
|
auto buf = (FileUploading *) obj;
|
|
|
|
|
auto readSize = size * nmemb;
|
|
|
|
|
buf->file.read(reinterpret_cast<char *>(ptr), readSize);
|
|
|
|
|
return readSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
|
|
|
|
{
|
|
|
|
|
auto fileTrans = (FileDownloading *) p;
|
|
|
|
|
curl_off_t speed = 0;
|
|
|
|
|
|
|
|
|
|
curl_easy_getinfo(fileTrans->curlHandle, CURLINFO_SPEED_DOWNLOAD_T, &speed);
|
|
|
|
|
|
|
|
|
|
fileTrans->completedSize += dlnow;
|
|
|
|
|
|
|
|
|
|
fileTrans->speed = speed;
|
|
|
|
|
qDebug() << std::format("Downloading: {} / {}, Speed: {}", fileTrans->completedSize, fileTrans->totalSize,
|
|
|
|
|
speed).c_str();
|
|
|
|
|
|
|
|
|
|
auto item = static_cast<FileItem>(*fileTrans);
|
|
|
|
|
QTimer::singleShot(0, qApp, [item]() {
|
|
|
|
|
FileTransferListModel::instance().setItem(item);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (fileTrans->paused) {
|
|
|
|
|
qDebug() << "Pause";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
fileTrans->completedSize -= dlnow;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int uploadInfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
|
|
|
|
{
|
|
|
|
|
auto fileTrans = (FileUploading *) p;
|
|
|
|
|
curl_off_t speed = 0;
|
|
|
|
|
curl_easy_getinfo(fileTrans->curlHandle, CURLINFO_SPEED_UPLOAD_T, &speed);
|
|
|
|
|
fileTrans->completedSize += ulnow;
|
|
|
|
|
fileTrans->speed = speed;
|
|
|
|
|
qDebug() << std::format("Uploading: {} / {}, Speed: {}", fileTrans->completedSize, fileTrans->totalSize,
|
|
|
|
|
speed).c_str();
|
|
|
|
|
|
|
|
|
|
auto item = static_cast<FileItem>(*fileTrans);
|
|
|
|
|
QTimer::singleShot(0, qApp, [item]() {
|
|
|
|
|
FileTransferListModel::instance().setItem(item);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (fileTrans->paused) {
|
|
|
|
|
qDebug() << "Pause";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
fileTrans->completedSize -= ulnow;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t writeDataCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
@ -60,44 +142,44 @@ FileTransferManager::FileTransferManager(QObject *parent)
|
|
|
|
|
: QObject(parent)
|
|
|
|
|
{
|
|
|
|
|
std::string resData;
|
|
|
|
|
if (CURLE_OK == httpGet("file/hello", resData))
|
|
|
|
|
if (CURLE_OK == httpGet("hello", resData))
|
|
|
|
|
qDebug() << "HTTP3 OK\n" << resData.c_str();
|
|
|
|
|
else
|
|
|
|
|
qDebug() << "HTTP3 FAILED";
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64_t completedSize = 0;
|
|
|
|
|
|
|
|
|
|
size_t readDataFunc(void *ptr, size_t size, size_t nmemb, void *file)
|
|
|
|
|
{
|
|
|
|
|
auto s = reinterpret_cast<QFile *>(file)->read(reinterpret_cast<char *>(ptr),
|
|
|
|
|
size * nmemb);
|
|
|
|
|
completedSize += s;
|
|
|
|
|
qDebug() << std::format("Uploading: {} / {}", completedSize,
|
|
|
|
|
reinterpret_cast<QFile *>(file)->size()).c_str();
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QString &ticket)
|
|
|
|
|
void
|
|
|
|
|
FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, const QString &ticket, const QString &fileName)
|
|
|
|
|
{
|
|
|
|
|
qDebug() << fileUrl.fileName();
|
|
|
|
|
|
|
|
|
|
QFile file = fileUrl.toLocalFile();
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
|
|
|
return;
|
|
|
|
|
auto fileSize = file.size();
|
|
|
|
|
QtConcurrent::run([fileUrl, fileId, ticket, fileName] {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto fileSize = QFileInfo(fileUrl.toLocalFile()).size();
|
|
|
|
|
|
|
|
|
|
FileItem item{false, fileId, fileName, 0, fileSize};
|
|
|
|
|
QTimer::singleShot(0, qApp, [item]() {
|
|
|
|
|
FileTransferListModel::instance().insertItem(item);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
uploadingMapMutex.lock();
|
|
|
|
|
auto [iter, _] = uploadingMap.emplace(item.id, (FileUploading) item);
|
|
|
|
|
uploadingMapMutex.unlock();
|
|
|
|
|
|
|
|
|
|
FileUploading &fileUploading = iter->second;
|
|
|
|
|
fileUploading.file = std::ifstream(fileUrl.toLocalFile().toLocal8Bit().toStdString(), std::ios::binary);
|
|
|
|
|
|
|
|
|
|
if (!fileUploading.file.is_open()) {
|
|
|
|
|
qDebug() << "Open File Failed";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CURLcode res;
|
|
|
|
|
CURL *curl = curl_easy_init();
|
|
|
|
|
if (!curl)
|
|
|
|
|
return;
|
|
|
|
|
fileUploading.curlHandle = curl_easy_init();
|
|
|
|
|
if (!fileUploading.curlHandle)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, (baseUrl + "file/" + fileId.toStdString()).c_str());
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);
|
|
|
|
|
curl_httppost *formpost = nullptr;
|
|
|
|
|
curl_httppost *lastptr = nullptr;
|
|
|
|
|
curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "ticket",
|
|
|
|
@ -113,21 +195,35 @@ void FileTransferManager::upload(const QUrl &fileUrl, const QString &fileId, con
|
|
|
|
|
|
|
|
|
|
curl_formadd(&formpost, &lastptr,
|
|
|
|
|
CURLFORM_COPYNAME, "data",
|
|
|
|
|
CURLFORM_STREAM, &file,
|
|
|
|
|
CURLFORM_STREAM, &fileUploading,
|
|
|
|
|
CURLFORM_CONTENTLEN, fileSize,
|
|
|
|
|
CURLFORM_END);
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_READFUNCTION, readDataFunc);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_URL, (baseUrl + fileId.toStdString()).c_str());
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3ONLY);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_HTTPPOST, formpost);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_READFUNCTION, readDataFunc);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_NOPROGRESS, false);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_XFERINFOFUNCTION, uploadInfo);
|
|
|
|
|
curl_easy_setopt(fileUploading.curlHandle, CURLOPT_XFERINFODATA, &fileUploading);
|
|
|
|
|
|
|
|
|
|
qDebug() << "Begin Upload";
|
|
|
|
|
completedSize = 0;
|
|
|
|
|
res = curl_easy_perform(curl);
|
|
|
|
|
|
|
|
|
|
res = curl_easy_perform(fileUploading.curlHandle);
|
|
|
|
|
if (res == CURLE_OK)
|
|
|
|
|
qDebug() << "Upload Success";
|
|
|
|
|
else
|
|
|
|
|
qDebug() << "Upload Failed";
|
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
|
|
curl_easy_cleanup(fileUploading.curlHandle);
|
|
|
|
|
curl_formfree(formpost);
|
|
|
|
|
file.close();
|
|
|
|
|
fileUploading.file.close();
|
|
|
|
|
|
|
|
|
|
uploadingMapMutex.lock();
|
|
|
|
|
uploadingMap.erase(fileId);
|
|
|
|
|
uploadingMapMutex.unlock();
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -151,66 +247,19 @@ curl_off_t getHttpFileSize(const std::string &url)
|
|
|
|
|
return fileLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct FileTransfering : FileItem
|
|
|
|
|
{
|
|
|
|
|
CURL *curlHandle = nullptr;
|
|
|
|
|
std::ofstream file;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
size_t receiveDataFunc(const void *ptr, size_t size, size_t nmemb, void *obj)
|
|
|
|
|
{
|
|
|
|
|
auto buf = (FileTransfering *) obj;
|
|
|
|
|
auto writeSize = size * nmemb;
|
|
|
|
|
buf->file.write(reinterpret_cast<const char *>(ptr), writeSize);
|
|
|
|
|
return writeSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int xferinfo(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
|
|
|
|
{
|
|
|
|
|
auto fileTrans = (FileTransfering *) p;
|
|
|
|
|
curl_off_t speed = 0;
|
|
|
|
|
|
|
|
|
|
curl_easy_getinfo(fileTrans->curlHandle, CURLINFO_SPEED_DOWNLOAD_T, &speed);
|
|
|
|
|
|
|
|
|
|
fileTrans->completedSize += dlnow;
|
|
|
|
|
|
|
|
|
|
fileTrans->speed = speed;
|
|
|
|
|
qDebug() << std::format("Downloading: {} / {}, Speed: {}", fileTrans->completedSize, fileTrans->totalSize,
|
|
|
|
|
speed).c_str();
|
|
|
|
|
|
|
|
|
|
auto item = static_cast<FileItem>(*fileTrans);
|
|
|
|
|
QTimer::singleShot(0, qApp, [item]() {
|
|
|
|
|
FileTransferListModel::instance().setItem(item);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (fileTrans->paused) {
|
|
|
|
|
qDebug() << "Pause";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fileTrans->completedSize -= dlnow;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::unordered_map<QString, FileTransfering> transferMap;
|
|
|
|
|
|
|
|
|
|
std::mutex transferMapMutex;
|
|
|
|
|
|
|
|
|
|
bool httpDownload(const std::string &url, const FileItem &item)
|
|
|
|
|
{
|
|
|
|
|
auto fileSize = getHttpFileSize(url);//获得文件大小。
|
|
|
|
|
if (fileSize != -1) {
|
|
|
|
|
if (fileSize == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
qDebug() << "FileSize: " << fileSize;
|
|
|
|
|
|
|
|
|
|
transferMapMutex.lock();
|
|
|
|
|
auto [iter, _] = transferMap.emplace(item.id, (FileTransfering) item);
|
|
|
|
|
transferMapMutex.unlock();
|
|
|
|
|
downloadingMapMutex.lock();
|
|
|
|
|
auto [iter, _] = downloadingMap.emplace(item.id, (FileDownloading) item);
|
|
|
|
|
downloadingMapMutex.unlock();
|
|
|
|
|
|
|
|
|
|
FileTransfering &obj = iter->second;
|
|
|
|
|
FileDownloading &obj = iter->second;
|
|
|
|
|
|
|
|
|
|
obj.curlHandle = curl_easy_init();
|
|
|
|
|
if (!obj.curlHandle)
|
|
|
|
@ -222,16 +271,15 @@ bool httpDownload(const std::string &url, const FileItem &item)
|
|
|
|
|
qDebug() << "Open File Failed";
|
|
|
|
|
curl_easy_cleanup(obj.curlHandle);
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_VERBOSE, 0L);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_HEADER, 0);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_NOPROGRESS, 1L);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_NOSIGNAL, 1L);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_URL, url.c_str());
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEDATA, (void *) &obj);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEFUNCTION, receiveDataFunc);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_WRITEFUNCTION, writeDataFunc);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_NOPROGRESS, false);//设为false 下面才能设置进度响应函数
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_XFERINFOFUNCTION, xferinfo);
|
|
|
|
|
curl_easy_setopt(obj.curlHandle, CURLOPT_XFERINFODATA, &obj);
|
|
|
|
@ -241,15 +289,12 @@ bool httpDownload(const std::string &url, const FileItem &item)
|
|
|
|
|
curl_easy_cleanup(obj.curlHandle);
|
|
|
|
|
obj.file.close();
|
|
|
|
|
|
|
|
|
|
transferMapMutex.lock();
|
|
|
|
|
transferMap.erase(obj.id);
|
|
|
|
|
transferMapMutex.unlock();
|
|
|
|
|
downloadingMapMutex.lock();
|
|
|
|
|
downloadingMap.erase(obj.id);
|
|
|
|
|
downloadingMapMutex.unlock();
|
|
|
|
|
|
|
|
|
|
return res == CURLE_OK;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileTransferManager::download(QString fileId)
|
|
|
|
|
{
|
|
|
|
@ -285,7 +330,7 @@ void FileTransferManager::download(QString fileId)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int64_t size = getHttpFileSize(fileUrl);
|
|
|
|
|
FileItem item{fileId, fileId, 0, size};
|
|
|
|
|
FileItem item{true, fileId, fileId, 0, size};
|
|
|
|
|
|
|
|
|
|
auto res = httpDownload(fileUrl, item);
|
|
|
|
|
|
|
|
|
@ -298,9 +343,9 @@ void FileTransferManager::download(QString fileId)
|
|
|
|
|
|
|
|
|
|
void FileTransferManager::pause(const QString &fileId)
|
|
|
|
|
{
|
|
|
|
|
transferMapMutex.lock();
|
|
|
|
|
transferMap[fileId].paused = true;
|
|
|
|
|
transferMapMutex.unlock();
|
|
|
|
|
downloadingMapMutex.lock();
|
|
|
|
|
downloadingMap[fileId].paused = true;
|
|
|
|
|
downloadingMapMutex.unlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString FileTransferManager::getFileMd5(const QUrl &fileUrl)
|
|
|
|
@ -358,7 +403,7 @@ void FileTransferManager::resume(const QString &fileId)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int64_t size = getHttpFileSize(fileUrl);
|
|
|
|
|
FileItem item{fileId, fileId, 0, size};
|
|
|
|
|
FileItem item{true, fileId, fileId, 0, size};
|
|
|
|
|
QTimer::singleShot(0, qApp, [this, item]() {
|
|
|
|
|
FileTransferListModel::instance().insertItem(item);
|
|
|
|
|
});
|
|
|
|
@ -371,3 +416,8 @@ void FileTransferManager::resume(const QString &fileId)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString FileTransferManager::getFileName(const QUrl &fileUrl)
|
|
|
|
|
{
|
|
|
|
|
return fileUrl.fileName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|