2010-05-10 50 views
7

我在Qt應用程序中遇到了一些麻煩;特別是與QNetworkAccessManager類。我正在嘗試使用QNetworkAccessManager的post()方法執行二進制文件的簡單HTTP上載。該文檔聲明我可以給QIODevice指向post(),並且該類將傳輸在QIODevice中找到的數據。這表明我應該能夠給post()一個指向QFile的指針。例如:使用QNetworkAccessManager的post()方法上傳文件

QFile compressedFile("temp"); 
compressedFile.open(QIODevice::ReadOnly); 
netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload")), &compressedFile); 

什麼似乎在Windows系統中,我發展,這是我的Qt應用程序推從一個QFile數據,但是不能夠完成請求的情況發生;它似乎坐在那裏等待更多的數據從文件中顯示出來。直到我手動終止應用程序時,發佈請求才會「關閉」,此時整個文件將顯示在服務器端。

從一些調試和研究,我認爲這是因爲QFile的read()操作在到達文件末尾時不返回-1。我認爲QNetworkAccessManager試圖從QIODevice讀取,直到它從read()中得到-1,此時它假定沒有更多數據並關閉請求。如果從read()中得到的返回代碼爲零,QNetworkAccessManager假定可能有更多的數據到來,所以它一直在等待那個假設的數據。

我已經用一些測試代碼證實,在你讀完文件後,QFile的read()操作只返回0。這似乎與QNetworkAccessManager的post()方法期望QIODevice行爲的方式不兼容。我的問題是:

  1. 這是QFile在Windows下工作的方式的某種限制?
  2. 是否有一些其他方式我應該使用QFile或QNetworkAccessManager通過post()推送文件?
  3. 這是不會工作的,我必須找到其他方式來上傳我的文件?

任何建議或提示,將不勝感激。

更新:事實證明,我有兩個不同的問題:一個在客戶端,一個在服務器端。在客戶端,我必須確保我的QFile對象在網絡事務期間保持閒置狀態。 QNetworkAccessManager的post()方法立即返回,但實際上並未立即完成。您需要將插槽附加到QNetworkAccessManager的finished()信號以確定POST何時實際完成。在我的情況下,很容易使QFile幾乎永久保持不變,但我還爲完成的()信號附加了一個插槽以檢查來自服務器的錯誤響應。

我重視的信號,這樣的槽:

connect(&netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(postFinished(QNetworkReply*))); 

當是時候把我的文件,我寫的帖子這樣的代碼(注意,compressedFile是我班的一員,也是如此不出門的範圍這個代碼後):

compressedFile.open(QIODevice::ReadOnly); 
netManager.post(QNetworkRequest(QUrl(httpDestination.getCString())), &compressedFile); 

從QNetworkAccessManager成品(QNetworkReply *)信號觸發我的postFinished(QNetworkReply *)方法。發生這種情況時,關閉compressedFile並刪除co​​mpressedFile表示的數據文件是安全的。出於調試的目的我也增加了一些printf()的聲明,以確認交易完成:

void CL_QtLogCompressor::postFinished(QNetworkReply* reply) 
{ 
    QByteArray response = reply->readAll(); 
    printf("response: %s\n", response.data()); 
    printf("reply error %d\n", reply->error()); 
    reply->deleteLater(); 
    compressedFile.close(); 
    compressedFile.remove(); 
} 

由於compressedFile不會立即關閉,不出門的範圍內,QNetworkAccessManager能夠爲取很多時間,因爲它喜歡傳輸我的文件。最終交易完成,我的postFinished()方法被調用。

我的其他問題(這也是導致我看到事務從未完成的行爲的原因)是我的Web服務器的Python代碼沒有正確設置POST字段,但這超出了我原始Qt問題的範圍。

回答

8

您在堆棧上創建compressedFile,並將指針傳遞給您的QNetworkRequest(最終您的QNetworkAccessManager)。只要你離開你所在的方法,compressedFile即將超出範圍。我很驚訝它不會崩潰在你身上,雖然行爲是未定義的。

您需要創建堆上的QFile

QFile *compressedFile = new QFile("temp"); 

你當然需要跟蹤它,然後delete一次後已經完成,或將其設置爲QNetworkReply的孩子因此,它在得到答覆後銷燬它被破壞:

QFile *compressedFile = new QFile("temp"); 
compressedFile->open(QIODevice::ReadOnly); 

QNetworkReply *reply = netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload")), compressedFile); 
compressedFile->setParent(reply); 
+1

爲了簡化內存管理開銷,您可以使QFile實例成爲QNetworkRequest對象的子對象,並且在請求對象被刪除時它將被自動刪除。 – VestniK 2010-05-11 08:47:16

+0

哦,很好的電話。我會編輯以反映這一點。 – 2010-05-11 12:49:19

+1

除...關閉外,QNetworkRequest不是QObject。但是,答覆是。 – 2010-05-11 12:59:16

3

您也可以使用信號調度堆分配文件自動刪除/插槽

QFile* compressedFile = new QFile(...); 
QNetworkReply* reply = Manager.post(...); 
// This is where the tricks is 
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()); 
connect(reply, SIGNAL(destroyed()), compressedFile, SLOT(deleteLater()); 

恕我直言,它是更多的本地化和封裝比保留在你的文件在外部類。

請注意,如果您有postFinished(QNetworkReply*)插槽,您必須先刪除第一個connect(),其中您必須在其中忘記調用reply->deleteLater()以使上述功能正常工作。

+0

謝謝,這是一個很好的替代建議。在我的案例中,後來我發現我並不總是想在發送完成後刪除compressedFile;取決於回覆的內容,我可能需要保留它。所以我一直保留在我的postFinished()插槽中刪除文件的邏輯,在那裏我可以檢查回覆以確定刪除文件是否安全。 – dbisdorf 2010-07-14 13:37:34