2016-11-05 105 views
0

我一樣使用文檔例如http://doc.qt.io/qt-4.8/qnetworkaccessmanager.html下載大文件:: readAll凍結幾秒鐘

我創建了一個startDownload

connect(pushButton, SIGNAL(clicked(bool)), this, SLOT(startDownload(bool))); 

startDownload(bool)我把這個:

file = new QFile("C:/foo/bar/bigfile.7z"); 
file->open(QIODevice::WriteOnly); 

QNetworkRequest request; 
request.setUrl(QUrl("http://localhost/bigfile.7z")); 
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0"); 

QNetworkReply *reply = manager->get(request); 

connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); 
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), 
     this, SLOT(slotError(QNetworkReply::NetworkError))); 
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), 
     this, SLOT(slotSslErrors(QList<QSslError>))); 

slotReadyRead我把這個:

file->write(reply->readAll()); 

但是,當下載到達結束時有一個小的凍結2秒,然後恢復正常,下載完成。只有當我嘗試傳輸的文件很大時,纔會出現此問題。

+0

我試着爲你的問題寫一個[minimal example](http://pastebin.com/et94k1Uk)。我計算了每個'file-> write(reply-> readAll())'調用的時間,大多數調用花費了0毫秒,最後一次調用(在下載結束時)沒有像描述的那樣有顯着差異(我試了我的網絡上的一個本地文件的代碼大小約爲1GB)。也許這是你的代碼中的另一件事情,讓事情變得緩慢?嘗試測量函數調用持續時間,以確保確實是導致問題的這個調用。 – Mike

+0

@Mike問題只出現在GUI應用程序中,例如'QMainWindow' –

+0

問題發生在GUI中,因爲回調需要花費很多時間才能完成並返回到事件循環。在我的測試中,我只是測量'file-> write(reply-> readAll());'花費的時間,以確保它在下載結束時真的需要2秒。但事實證明,這並不是真的需要那麼長時間,你的輸出是不同的? – Mike

回答

1

試圖使用@Mike代碼後,我注意到,在readyRead最後讀出的數據比以前的,這使得在文件中寫入它慢要高得多:

without setReadBufferSize

最後兩個讀數:

  1. 46080000字節 - 需要約1.6秒的時間才能寫入。
  2. 227323951字節 - 需要約2.7秒的時間才能寫入。

它根據網絡類型和速度的不同而不同,從而使緩衝區變得非常好。

在GUI應用程序中導致「凍結」約4秒。

對於LIMITE緩衝液I中使用QNetworkReply::setReadBufferSize,看到的區別:

with setReadBufferSize

讀數是1048576個字節 - 需要毫秒2和10之間來寫。

+0

@KubaOber我明白,但它是唯一的工作,我遵循'@ ddriver'的指導,你能告訴我一個更好的方法嗎? –

+0

您正在泄漏網絡訪問管理器的實例。由於下載工作人員總是需要網絡訪問管理器,因此您可以將其添加爲成員並按值保存,每次調用process()時都不需要重新創建它。 –

+0

我會在某個地方給出另一個答案:) –

2

這是預期的行爲。 QIODevice::readAll()將阻塞該線程,直到下載完成。根據磁盤速度和緩存策略,可能會阻止QFile::write()。如果文件足夠大,readAll()方法也可能消耗相當多的RAM。

最簡單的解決方案是使用read()而不是readAll()以較小的塊下載文件。現在說,沒有簡單的方法來找到從網絡讀取並寫入磁盤的完美緩衝區大小;這取決於網絡連接如何響應與磁盤寫入速度的關係。

+0

這是有道理的,可以舉個例子嗎?謝謝! –

+0

或者如果你想使用阻塞API,不要阻塞主線程 - 使用工作線程。 – dtech

+0

@ddriver謝謝,但我嘗試使用一個線程,甚至提出了另一個問題,我得到了幾個密切的投票和downvotes和意見中的很多reclameções沒有任何意義,我刪除了問題。 –