[英]Resuming a Failed HTTP Download With Qt & QNetworkRequest
我正在嘗試為我正在開發的應用程序添加自動更新功能。 我已經將這個功能基於Qt HTTP示例 (基於我的意思是我完全復制了這個例子然后從那里開始)。 它正在下載ZIP文件,然后提取其內容以修補應用程序。
有時,下載時,連接將失敗,下載將停止。 為了更加用戶友好,我想我會向下載器添加自動重啟功能,如果失敗則會嘗試重啟下載。
以下是我的代碼的亮點 - 方法名稱與示例中的方法名稱匹配:
void Autopatcher::httpReadyRead()
{
//file is a QFile that is opened when the download starts
if (file) {
QByteArray qba = reply->readAll();
//keep track of how many bytes have been written to the file
bytesWritten += qba.size();
file->write(qba);
}
}
void Autopatcher::startRequest(QUrl url)
{
//doResume is set in httpFinished() if an error occurred
if (doResume) {
QNetworkRequest req(url);
//bytesWritten is incremented in httpReadyRead()
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(bytesWritten) + "-";
req.setRawHeader("Range",rangeHeaderValue);
reply = qnam.get(req);
} else {
reply = qnam.get(QNetworkRequest(url));
}
//slot connections omitted for brevity
}
//connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(fileGetError(QNetworkReply::NetworkError)));
void Autopatcher::fileGetError(QNetworkReply::NetworkError error) {
httpRequestAborted = true;
}
void Autopatcher::httpFinished() {
//If an error occurred
if (reply->error()) {
//If we haven't retried yet
if (!retried) {
//Try to resume the download
doResume=true;
//downloadFile() is a method that handles some administrative tasks
//like opening the file if doResume=false
//and calling startRequest() with the appropriate URL
QTimer::singleShot(5000,this,SLOT(downloadFile()));
}
//If we have retried already
else {
//Give up :(
if (file) {
file->close();
file->remove();
delete file;
file = 0;
}
}
//If no error, then we were successful!
} else {
if (file) {
file->close();
delete file;
file = 0;
}
//Apply the patch
doPatch();
}
reply->deleteLater();
reply = 0;
}
現在,如果下載正常完成而沒有中斷,它可以正常工作。 ZIP完美提取。 但是,如果連接失敗並且應用程序重新啟動下載,它會完成下載,我可以看到7-zip中的ZIP文件的所有內容,但我無法提取它們(7-zip說了一些“試圖在文件開始之前移動指針)。
我假設我在某個地方做了一個簡單的逐個錯誤,就像在HTTP Range標題中一樣。 我已經看到了如何在這個博客上暫停和恢復下載的示例,但是他在暫停時將流的內容寫入文件,而我將它們流式傳輸到httpReadyRead
的文件中。 我不知道這是否會導致問題。
為了測試,我一直在使用Sysinternals TCPView在下載過程中切斷TCP連接。 我不確定如何進一步調試,所以如果有更多信息有用,請告訴我!
所以今天我進行了深入研究 我原本以為不間斷和中斷版本的文件大小是相同的+ - 幾個字節,但我錯了。 我下載了兩個版本的文件,大小偏差大約2兆字節。
所以,我使用VBinDiff比較它們(如果你不害怕控制台界面,這是一個很好的實用工具),這就是我發現的:
這證實了當我嘗試重新啟動下載時, 我正在跳過遠程文件的重要部分 。 不確定發生了什么,我決定檢查bytesWritten
的值,我用它來跟蹤我寫入文件的字節數。 這個值是我寫入Range Request Header的值,所以它的值必須是不正確的。 (請參閱問題中的httpReadyRead()
函數)。
所以我在設置Range Request Header之前添加了下面的代碼:
file->flush();
bytesWritten = file->size();
調試代碼,我很驚訝地發現
bytesWritten = 28,947,923
file->size() = 26,589,171
確認bytesWritten
值不正確。 事實上,當我使用文件大小而不是bytesWritten
值時,下載能夠重新啟動並成功完成!
我不會再深入了,因為這對我有用。 實際上,這將允許重新啟動應用程序實例之間的下載,因此在我看來這是一種更好的方法。
tl; dr不要跟蹤寫入文件的字節數。 只需在重新啟動失敗的下載時檢查文件大小。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.