目錄
開發過程中遇到了一些坑,花了半天時間搞。出現了很多理論上不應該出現的bug。
做這個的目的,主要是爲了鞏固一下對Qt網絡部分接口的使用。
思路主要是,解析URL——>獲取響應頭——>獲取響應狀態碼——>請求下載——>回讀當前文件字節數進行斷點續傳。
需要注意,訪問https前,需要添加libcrypto-1_1.dll與libssl-1_1.dll到Qt\5.14.0\mingw73_32\bin或者exe目錄下。
下載地址:http://slproweb.com/products/Win32OpenSSL.html
需要注意一下32位和64位,應下載與自己Qt編譯器位數一致的版本。下載好後直接安裝,然後到安裝目錄拷貝即可。
一、gitHub地址
https://github.com/KindMans/HttpDownLoad
二、功能
支持輸入url網址進行在線下載,具備斷點續傳功能。
後期擴展:結合qml樹列表與多線程,模擬出迅雷的下載效果。
三、目前存在的問題
QEventLoop *loop = new QEventLoop;
connect(m_netWorkManager, SIGNAL(finished(QNetworkReply*)), loop, SLOT(quit()));
m_reply = m_netWorkManager->get(request);
loop->exec();
通過以上get訪問到需要跳轉的url時,速度明顯快於不需要跳轉的。具體原因還不清楚。
四、界面效果
粗略的繪製了一下界面,比較醜,主要以實現效果爲主。
五、主要代碼
啓動下載:
void Http::startDownLoad(const QString &url)
{
m_url = url;
if(m_url.isEmpty()) return;
if(!m_IsDownloading)
{
//獲取請求頭
QNetworkRequest request;
QUrl url = QUrl(m_url);
request.setUrl(url);
m_fileName = url.fileName();
qDebug()<<"fileName = "<<m_fileName;
m_reply = m_netWorkManager->head(request);
m_state = requestHead;
getCurrentFileSize();
connect(m_reply,SIGNAL(finished()),this,SLOT(onfinishedRequest()));
}
}
暫停下載:
void Http::stopDownLoad()
{
if(m_reply == nullptr) return;
disconnect(m_reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
disconnect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(onError(QNetworkReply::NetworkError)));
disconnect(m_reply,SIGNAL(finished()),this,SLOT(onfinishedRequest()));
m_reply->abort();
m_reply->deleteLater();
m_file.close();
getCurrentFileSize();
m_IsDownloading = false;
}
文件大小回讀:
void Http::getCurrentFileSize()
{
QFileInfo fileInfo(m_fileName);
if(fileInfo.exists())
{
m_currentLoadedBytes = fileInfo.size();
}
else
{
m_currentLoadedBytes = 0;
}
}
槽函數onfinishedRequest():
void Http::onfinishedRequest()
{
if(m_reply==nullptr) return;
if(m_state == requestHead)
{
m_fileSize = m_reply->rawHeader("Content-Length").toInt();
qDebug()<<"m_fileSize = "<<m_fileSize;
if(m_currentLoadedBytes == m_fileSize)
{
qDebug()<<"文件已經存在!";
return;
}
QEventLoop *loop = new QEventLoop;
connect(m_netWorkManager, SIGNAL(finished(QNetworkReply*)), loop, SLOT(quit()));
QNetworkRequest request;
request.setUrl(m_url);
m_reply = m_netWorkManager->get(request);
loop->exec();
//獲取狀態碼
m_statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<"statusCode="<<m_statusCode;
m_state = requestBody;
}
QNetworkRequest request;
if(m_statusCode==200)
{
request.setUrl(m_url);
}
else if(m_statusCode == 302) //存在轉調url
{
//獲取實際下載地址
QUrl realUrl = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
request.setUrl(realUrl);
}
else
{
return;
}
QString downLoadSize = QString::number(m_fileSize);
QString selectSize = QString("bytes=%1-%2").arg(m_currentLoadedBytes).arg(downLoadSize);
request.setRawHeader("Range",selectSize.toLatin1());
m_reply = m_netWorkManager->get(request);
connect(m_reply,SIGNAL(finished()),this,SLOT(onfinishedRequest()));
connect(m_reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(onError(QNetworkReply::NetworkError)));
}
槽函數onReadyRead():
void Http::onReadyRead()
{
if(m_reply==nullptr) return;
if(!m_file.isOpen())
{
m_file.setFileName(m_fileName);
m_file.open(QIODevice::WriteOnly|QIODevice::Append);
}
m_file.write(m_reply->readAll());
m_downLoadedBytes =m_file.size();
emit fileDownloadProgress(m_downLoadedBytes, m_fileSize);
if(m_file.size() == m_fileSize)
{
qDebug()<<"download finished!";
stopDownLoad();
}
}