libCURL在Qt中的使用


  最近業余時間在搞Qt,其中的一個功能是實現FTP的上傳下載。

  之前版本的Qt提供了一個FTP操作的類,但是5.x(4.x?)之后因為性能問題就棄用了。貌似CSDN上有人發帖問過這個問題,記得應該是put多大的文件時導致占用內存過大。現在Qt的官方手冊推薦使用QNetworkAccessManager用於TCP/IP以及FTP的傳輸。說實話這玩意對於FTP的兼容並不好。於是想到了CURL。

  網上關於CURL和libCURL的文章很多,但是用於FTP操作的文章大多數停留於試驗性的代碼。別的不說,忽略了一個比較重要的問題就是FTP的傳輸模式。

  FTP有兩種傳輸模式,ASCII和Binary,即TYPE A 和TYPE I。大家的文章里面都沒有提到這個事情。我用libCURL主要是傳輸文本文件,所以這個問題一下就浮現出來了。

  libCURL的下載什么的就不說了,到官網上找windows的包就可以,記得下載develop版本。

  在QT里面使用動態庫,首先要做的肯定是修改pro文件。

 

1 LIBS    += C:\your-curl-lib-location\curl-7.34.0-devel-mingw32\lib\libcurldll.a
2 
3 INCLUDEPATH += C:\your-curl-lib-location\curl-7.34.0-devel-mingw32\include

  首先指定靜態庫位置,接着指定include路徑。這個沒什么好說的。然后把curl路徑下的bin目錄中的dll拷貝到debug文件夾。否則run的時候會“莫名其妙”的停止。

  這里使用的是CURL的easy借口(貌似網上的文章都是使用的這個)。

  先來看下下載代碼。

 1     CURLcode ret;
 2     curl_global_init(CURL_GLOBAL_ALL);
 3     CURL* curl = curl_easy_init(); 
 4 
 5     curl_easy_setopt(curl,CURLOPT_URL,remoteFile.toLatin1().data()); 
 6     curl_easy_setopt(curl,CURLOPT_USERPWD,"username:password"); 
 7     ret = curl_easy_setopt(curl,CURLOPT_WRITEDATA,localFile); 
 8     ret = curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curlWriteFunction);
 9 
10     curl_easy_setopt(curl,CURLOPT_TRANSFERTEXT,1);
11 
12     curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
13     ret = curl_easy_perform(curl);
14 
15     curl_easy_cleanup(curl);
16     curl_global_cleanup();

  解釋幾點。

    1)  聲明CURLcode,獲取curl接口的返回值。

    2)  使用 curl_global_init對CURL進行初始化。傳遞CURL_GLOBAL_ALL進去表明對所有平台進行初始化,這個是最保險的,也保證了代碼的可移植性。

    3)  curl_easy_setopt函數對curl的選項進行設定。

         該函數的第一個參數為curl指針,第二個為CURL選項名稱(其實就是個整數),第三個為實際選項的值,可以是字符串、指針,函數指針等,根據不同的選項有不同的值。

          CURLOPT_URL:  設定需要上傳/下載的文件URL。

          CURLOPT_USERPWD:設定登錄服務器的賬號密碼,格式為user:passwd。

          CURLOPT_WRITEDATA:傳遞DATA的指針。引用一句被用爛的話,“CURL對該指針不做任何處理,只做傳遞作用。”。這個指針有什么用,我們一會兒再看。

          CURLOPT_WRITEFUNCTION:設定寫文件的函數。當curl從服務器獲取數據之后,便會調用這個函數做寫入處理。函數原型如下:

            size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);

            看見最后一個參數了米有?這個參數就是CURLOPT_WRITEDATA中指定的指針。也就是,如果你想指定幾個參數貫穿CURL“獲取-寫入"這個過程,就把他們的指針通過CURLOPT_WRITEDATA傳給CURL。這里我給了個QFile指針進去。

            需要說明的一點是,如果在C++中只用CURL,這個函數在類中應當被聲明為static的。

        CURLOPT_TRANSFERTEXT: 指定CURL的FTP傳輸模式。后面的值為1,則為ASCII,0位二進制傳輸。如果使用了錯誤的傳輸模式,在下載的過程中,服務器可能給你一個553。最開始這個錯誤讓我很莫名其妙。因為我使用了CURLOPT_PREQUOTE去追加了一個TYPE A的命令給CURL。理論上應該是可行的,但實際上文件傳輸總是到一半就被服務器關閉鏈接。猜測這個可能與CURL的多線程有關?僅僅做猜測了。

        CURLOPT_VERBOSE 設定調試級別。

        在libCURL的網站上,有一個鏈接專門解釋這些宏。

        

1 http://curl.haxx.se/libcurl/c/curl_easy_setopt.html

 

 

    4)   curl_easy_perform 執行curl動作。默認為下載。

 

  對CURL設置結束后,就可以編寫之前指定的寫入函數了。

     

1 int FTPThread::curlWriteFunction(void *buffer, size_t size, size_t nmemb, void *stream){
2     QFile* file = static_cast<QFile*>(stream);
3     if(!file->isOpen()){
4         file->open(QFile::ReadWrite | QIODevice::Text);
5     }
6     return file->write((char*)buffer,nmemb);
7 
8 }

  在QT中,這個函數是很容易被實現的。只要確保返回值為實際的寫入字節就可以,因為CURL會檢查返回值,如果與nmemb不相等的話,會報錯。

 

  上傳的代碼與下載很類似,直接上代碼了。

 

 1 CURLcode FTPThread::CURLUpload(){
 2     CURLcode ret;
 3     curl_global_init(CURL_GLOBAL_ALL);
 4     CURL* curl = curl_easy_init();
 5     QFile* localFile = new QFile("FTPCache/"+fileName);
 6     qDebug()<<localFile->fileName()<<endl;
 7     QString remoteFile = "ftp://your.ftp.server/"+fileName;
 8 
 9     if(!localFile->open(QFile::ReadWrite | QIODevice::Text)){
10         qDebug()<<"open file fail"<<endl;
11         return CURLE_READ_ERROR;
12     }
13 
14     curl_easy_setopt(curl,CURLOPT_URL,remoteFile.toLatin1().data());
15     curl_easy_setopt(curl,CURLOPT_USERPWD,"username:password");
16 
17     curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
18     curl_easy_setopt(curl,CURLOPT_FTP_CREATE_MISSING_DIRS,CURLFTP_CREATE_DIR);
19     ret = curl_easy_setopt(curl,CURLOPT_READDATA,localFile);
20     curl_easy_setopt(curl,CURLOPT_INFILESIZE_LARGE,localFile->size());
21     ret = curl_easy_setopt(curl,CURLOPT_READFUNCTION,curlReadFunction);
22 
23     //Timeout
24     curl_easy_setopt(curl,CURLOPT_FTP_RESPONSE_TIMEOUT,60000);
25 
26 
27     //Set transfer mode to ASCII
28     curl_easy_setopt(curl,CURLOPT_TRANSFERTEXT,1);
29     curl_easy_setopt(curl,CURLOPT_BUFFERSIZE,200);
30 
31 
32     curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
33     ret = curl_easy_perform(curl);
34     if(ret!=CURLE_OK)
35             qDebug()<<"perform fail"<<endl;
36 
37     curl_easy_cleanup(curl);
38     curl_global_cleanup();
39     localFile->close();
40     return ret;
41 }

  只需說明一點,curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); 指定了CURL的動作為上傳。

  讀取文件的函數原型如下:

 

1 size_t read_callback(char *buffer, size_t size, size_t nitems, void *instream);

 

  實現如下:

1 int FTPThread::curlReadFunction(void *buffer, size_t size, size_t nmemb, void *stream){
2     QFile* file = static_cast<QFile*>(stream);
3     return file->read((char*)buffer,size*nmemb);
4 }

 

  全文完。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM