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 }

  全文完。

时间: 2024-10-11 04:50:32

libCURL在Qt中的使用的相关文章

第三十八课、Qt中的事件处理(上)

一.图形界面应用程序的消息处理模型 二.Qt的事件处理 1.Qt平台将系统产生的消息转换为Qt事件(每一个系统消息对象Qt平台的一个事件) (1).Qt事件是一个QEvent的对象 (2).Qt事件用于描述程序内部或者外部发生的动作 (3).任意的QObject对象都具备事件处理的能力 2.GUI应用程序的事件处理方式 (1).Qt事件产生后立即被分发到QWidget对象 (2).QWidget中的event(QEvent*)进行事件处理 (3).event()根据事件类型调用不同的事件处理函数

第38课 Qt中的事件处理(上)

1. GUI程序原理回顾 (1)图形界面应用程序的消息处理模型 (2)思考:操作系统发送的消息如何转变为Qt信号 2. Qt中的事件处理 (1)Qt平台将系统产生的消息转换为Qt事件 ①Qt事件是一个QEvent的对象 ②Qt事件用于描述程序内部或外部发生的动作 ③任意的QObject对象都具备事件处理的能力 (2)GUI应用程序的事件处理方式 ①Qt事件产生后立即被分发到QWidget对象 ②QWidget中的event(QEvent*)进行事件处理 ③event()根据事件类型的不同,调用不

[转]Qt中ui文件的使用

用designer设计的*.ui文件可以通过uic工具转换为*.h文件(在编译时也会自动生成这样一个ui_*.h文件),有了这个.h文件就可以直接按照纯C++的方式对其中的类进行调用.ui文件的使用就是利用默认工具uic自动产生一个类,然后用该类的setui函数加载界面到相应的对象上.       .ui文件的使用有三种形式:第一种是直接使用,第二种是定义一个新类,声明一个ui子对象,利用该对象来加载界面,第三种是将ui作为基类派生新的类. 借用一个例程分析如下: 工程及界面          

第47课 Qt中的调色板

1. QPalette类 (1)QPalette类提供了绘制QWidget组件的不同状态所使用的颜色. (2)QPalette对象包含了3个状态的颜色描述 ①激活颜色组(Active):组件获得焦点使用的颜色搭配方案 ②非激活颜色组(Inactive):组件失去焦点使用的颜色方案 ③失效颜色组(Disabled):组件处于不可用状态使用的颜色方案 2.QPalette类中颜色组用途 (1)QPalette类中的颜色组定义了组细节的颜色值 (2)QPalette::ColorRole中的常量值用于

Qt中如何写一个model(自定义一个RowNode,我没有碰到过)

在qt中,用到最多就是model/view的结构来表示数据层及表示层的关系.model用于给view提供数据.那如何来实现一个简单的树形model呢. 实现一个自己的model需要重载以下的方法: [cpp] view plain copy QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVaria

第39课 Qt中的事件处理(下)

1. 事件的传递过程 (1)操作系统检测到用户动作时,会产生一条系统消息,该消息被发送到Qt应用程序 (2)Qt应用程序收到系统消息后,将其转化为一个对应的事件对象,并将其分发出去. (3)事件对应被分发到当前正在操作的窗口部件上,该窗口部件会调用event函数来处理,然后,在这个函数内部又会调用其他的子函数(如KeyPress或mouseReleaseEvent)来进行具体的处理. (4)event函数处理完后,可能会将当前事件传递给父组件(parent)对象.但这个过程只是一种可能,也就是有

第21课 Qt中的标准对话框(下)

1. 字体对话框:QFontDialog (1)Qt中提供了预定义的字体对话框类(QFontDialog) (2)QColorDialog类用于提供选择字体的对话框部件 (3)字体对话框的使用方式(见后面的实例分析) (4)QFontDialog中的实用函数: QFontDialog::getFont 2. 进度对话框:QProgressDialog (1)Qt中提供了预定义的进度对话框类(QProgressDialog) (2)QProgressDialog类用于显示进度信息 (3)QProg

Qt中Ui名字空间以及setupUi函数的原理和实现

用最新的QtCreator选择GUI的应用会产生含有如下文件的工程 下面就简单分析下各部分的功能. .pro文件是供qmake使用的文件,不是本文的重点[不过其实也很简单的],在此不多赘述. 所以呢,还是从main开始, [cpp] view plain copy #include <QtGui/QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(ar

Qt中各个widget前后位置的设定(在Qt中,所有问题都要一分为二,QWidget体系和QGraphicsWidget体系)

这两天在总结一些以往project中遇到的问题,正好别组有同事问我关于Qt中各个widget窗口的前后位置是如何定义的,这里就总结一下: 在Qt中,所有问题都要一分为二,讨论两种不同的情况:一个是最常用的QWidget体系,而另外一个则是QGraphicsWidget体系. ①:在常用的QWidget体系中,设置前后位置是主要是用raise(),underStack(),hide()函数. 理论为:在Qt中每个widget窗口都维持着自己的一个创建栈(widget's stack.),栈里边存放