首页 > 代码库 > libCURL在Qt中的使用

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.a2 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     //Timeout24     curl_easy_setopt(curl,CURLOPT_FTP_RESPONSE_TIMEOUT,60000);25 26 27     //Set transfer mode to ASCII28     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 }

 

  全文完。

 

libCURL在Qt中的使用