找回密码
 立即注册
收起左侧

第33篇 网络(三)FTP(一)

30
回复
32074
查看
[复制链接]
累计签到:1564 天
连续签到:1 天
来源: 2013-9-4 14:52:46 显示全部楼层 |阅读模式
FTP(一)


版权声明

该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处!



导语

上一节我们讲述了HTTP的编程,这一节讲述与其及其相似的FTP的编程。FTPFileTransfer Protocol,也就是文件传输协议。FTP的主要作用,就是让用户连接上一个远程计算机,查看远程计算机有哪些文件,然后把文件从远程计算机上拷贝到本地计算机,或者把本地计算机的文件送到远程计算机上。


环境:Windows Xp + Qt 4.8.5+QtCreator 2.8.0



提示:Qt 5中默认不再包含该类,可以到网站下载页面的“其他”中下载该类源码使用。



目录

一、简介
二、实现简单的文件下载


正文


一、简介

在Qt中,我们可以使用上一节讲述的QNetworkAccessManager和QNetworkReply类来进行FTP 程序的编写,因为它们用起来很简单。但是,对于较复杂的FTP操作,Qt还提供了QFtp类,利用这个类,我们很容易写出一个FTP客户端程序。下面我们先在帮助中查看这个类。



QFtp中,所有的操作都对应一个特定的函数,我们可以称它们为命令。如connectToHost()连接到服务器命令,login()登录命令,get()下载命令,mkdir()新建目录命令等。因为QFtp类以异步方式工作,所以所有的这些函数都不是阻塞函数。也就是说,如果一个操作不能立即执行,那么这个函数就会直接返回,直到程序控制权返回Qt事件循环后才真正执行,它们不会影响界面的显示。

所有的命令都返回一个int 型的编号,使用这个编号让我们可以跟踪这个命令,查看其执行状态。当每条命令开始执行时,都会发出commandStarted()信号,当该命令执行结束时,会发出commandFinished()信号。我们可以利用这两个信号和命令的编号来获取命令的执行状态。当然,如果不想执行每条命令都要记下它的编号,也可以使用currentCommand()来获取现在执行的命令,其返回值与命令的对应关系如下图。



二、实现简单的文件下载

下面我们先看一个简单的FTP客户端的例子,然后对它进行扩展。在这个例子中我们从FTP服务器上下载一个文件并显示出来。

1.我们新建Qt Gui应用。
项目名次为“myFtp”,基类选择QWidget,类名保持Widget即可。完成后打开muFtp.pro文件,在上面添加一行:QT += network,然后保存该文件。

2.修改widget.ui文件。
在其中添加一个TextBrowser 和一个 Label,效果如下。

3.main.cpp中进行修改。
为了在程序中可以使用中文,我们在main.cpp中添加头文件#include <QTextCodec>
并在main()函数中添加代码:
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());


4.widget.h中进行修改。
先添加头文件:#include <QFtp>
再在private中定义对象:QFtp *ftp;
添加私有槽函数:
private slots:
    void ftpCommandStarted(int);
void ftpCommandFinished(int,bool);

5.widget.cpp中进行更改。
1)在构造函数中添加代码:
  1. ftp = new QFtp(this);
  2. ftp->connectToHost("ftp.qt-project.org"); //连接到服务器
  3. ftp->login();   //登录
  4. ftp->cd("qt/source");   //跳转到“qt”目录下的source目录中
  5. ftp->get("INSTALL");  //下载“INSTALL”文件
  6. ftp->close();   //关闭连接

  7. // 当每条命令开始执行时发出相应的信号
  8. connect(ftp,SIGNAL(commandStarted(int)),
  9.         this,SLOT(ftpCommandStarted(int)));

  10. // 当每条命令执行结束时发出相应的信号
  11. connect(ftp,SIGNAL(commandFinished(int,bool)),
  12.         this,SLOT(ftpCommandFinished(int,bool)));
复制代码
我们在构造函数里执行了几个FTP的操作,登录站点,并下载了一个文件。然后又关联了两个信号和槽,用来跟踪命令的执行情况。


2)实现槽函数:
void Widget::ftpCommandStarted(int)
{
    if(ftp->currentCommand() == QFtp::ConnectToHost){
       ui->label->setText(tr("正在连接到服务器..."));
    }
    if (ftp->currentCommand() == QFtp::Login){
       ui->label->setText(tr("正在登录..."));
    }
    if (ftp->currentCommand() == QFtp::Get){
       ui->label->setText(tr("正在下载..."));
    }
    else if (ftp->currentCommand() == QFtp::Close){
       ui->label->setText(tr("正在关闭连接..."));
    }
}

每当命令执行时,都会执行ftpCommandStarted()函数,它有一个参数int id,这个id就是调用命令时返回的id,如int loginID= ftp->login(); 这时,我们就可以用if(id == loginID)来判断执行的是否是login()函数。但是,我们不想为每个命令都设置一个变量来存储其返回值,所以,我们这里使用了ftp->currentCommand() ,它也能获取当前执行的命令的类型。在这个函数里我们让开始不同的命令时显示不同的状态信息。

void Widget::ftpCommandFinished(int,bool error)
{
    if(ftp->currentCommand() == QFtp::ConnectToHost){
       if(error)
           ui->label->setText(tr("连接服务器出现错误:%1")
                               .arg(ftp->errorString()));
       else ui->label->setText(tr("连接到服务器成功"));
    }
    if (ftp->currentCommand() == QFtp::Login){
       if(error)
           ui->label->setText(tr("登录出现错误:%1")
                               .arg(ftp->errorString()));
       else ui->label->setText(tr("登录成功"));
    }
    if (ftp->currentCommand() == QFtp::Get){
       if(error)
           ui->label->setText(tr("下载出现错误:%1")
                               .arg(ftp->errorString()));
       else {
           ui->label->setText(tr("已经完成下载"));
           ui->textBrowser->setText(ftp->readAll());
       }
    }
    else if (ftp->currentCommand() == QFtp::Close){
       ui->label->setText(tr("已经关闭连接"));
    }
}

这个函数与ftpCommandStarted()函数相似,但是,它是在一个命令执行结束时执行的。它有两个参数,第一个intid,就是调用命令时返回的编号,我们在上面已经讲过了。第二个是bool error,它标志现在执行的命令是否出现了错误。如果出现了错误,那么error true ,否则为false。我们可以利用它来输出错误信息。在这个函数中,我们在完成一条命令时显示不同的状态信息,并显示可能的出错信息。在if (ftp->currentCommand() == QFtp::Get) 中,也就是已经完成下载时,我们让textBrowser显示下载的信息。


6.运行程序,效果如下。



7.出错演示。
下面我们演示一下出错时的情况。
将构造函数中的代码ftp->login();改为ftp->login("tom","123456");
这时我们再运行程序:

可以看到,它输出了错误信息,指明了错误的指令和出错的内容。其实我们设置的这个错误,也是想告诉大家,在FTP中如果没有设置用户名和密码,那么默认的用户名应该是anonymous,这时密码可以任意填写,而使用其他用户名是会出错的。



结语

     在下一节中,我们将会对这个程序进行扩展,让它可以浏览服务器上的所有文件,并进行下载。




涉及的源码下载:



上一篇: 第32篇 网络(二)HTTP

下一篇: 第34篇 网络(四) FTP(二)

返回:系列教程目录





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

累计签到:15 天
连续签到:1 天
2013-10-7 20:49:16 显示全部楼层
亚飞老师 qt5中没有qftp了。。。怎么办
回复 支持 反对

使用道具 举报

累计签到:15 天
连续签到:1 天
2013-10-7 20:54:26 显示全部楼层
只是路过 发表于 2013-10-7 20:49
亚飞老师 qt5中没有qftp了。。。怎么办

我找到了一个教程就是自己编译添加qftp的
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-10-8 09:39:53 显示全部楼层
只是路过 发表于 2013-10-7 20:54
我找到了一个教程就是自己编译添加qftp的

嗯。你可以试着做做,如果成功了可以整理成教程发过来。
回复 支持 反对

使用道具 举报

累计签到:11 天
连续签到:1 天
2013-10-8 13:48:53 显示全部楼层
Qt 5  在.pro文件里面添加了Qt += network,但是在.h文件中不起作用,是不是因为Qt 5不支持这个呢?
回复 支持 反对

使用道具 举报

累计签到:15 天
连续签到:1 天
2013-10-8 19:10:30 显示全部楼层
yafeilinux 发表于 2013-10-8 09:39
嗯。你可以试着做做,如果成功了可以整理成教程发过来。

怎么弄呢 要发帖吗 那个我搞定了 不过和网上教程方法有点差别
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-10-8 19:34:27 显示全部楼层
只是路过 发表于 2013-10-8 19:10
怎么弄呢 要发帖吗 那个我搞定了 不过和网上教程方法有点差别

发帖吧,发到开放区的文章教程板块。
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2013-10-8 19:38:31 显示全部楼层
danran0214 发表于 2013-10-8 13:48
Qt 5  在.pro文件里面添加了Qt += network,但是在.h文件中不起作用,是不是因为Qt 5不支持这个呢? ...

是QT += network

大写的T,然后保存 .pro文件。
回复 支持 反对

使用道具 举报

累计签到:8 天
连续签到:1 天
2013-10-23 19:47:53 显示全部楼层
很好,跟着楼主的做,感觉受益匪浅
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2014-5-6 22:06:13 显示全部楼层
老师,在Qt5 中没有Qftp ,网上有window下自己编译的教程,可是在linux下如何实现呢
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2014-5-20 22:24:39 显示全部楼层
albzcj 发表于 2014-5-6 22:06
老师,在Qt5 中没有Qftp ,网上有window下自己编译的教程,可是在linux下如何实现呢 ...

类似的,可以使用QNetworkAccessManager来做,也可以下载QFtp的源码重新编译
回复 支持 反对

使用道具 举报

累计签到:35 天
连续签到:1 天
2014-5-21 20:44:39 显示全部楼层
只是路过 发表于 2013-10-7 20:54
我找到了一个教程就是自己编译添加qftp的

你好,我看到您的回复说搞定了自己编译添加qftp的方法。 但是我在qter没有找到您的教程。
可否告知自己编译的方法?  新手表示没有头绪。
回复 支持 反对

使用道具 举报

尚未签到

2014-6-24 15:00:00 显示全部楼层
运行错误QObject: Cannot create children for a parent that is in a different thread.
(Parent is Widget(0x23fe0c), parent's thread is QThread(0x38aa10), current thread is QThread(0x1390f398)
回复 支持 反对

使用道具 举报

累计签到:41 天
连续签到:1 天
2014-11-2 13:07:36 显示全部楼层
Qt 5.0 QFtp 解决
http://yunpan.cn/cs57vxkXD52Hi   提取码(2e38)

最后在main.cpp 加上  #pragma comment (lib,"Qt5Ftp.lib")
回复 支持 反对

使用道具 举报

尚未签到

2015-12-22 10:42:45 显示全部楼层
弱弱问一句:连接不上ftp.qt-project.org怎么办,  label一直显示正在连接
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2015-12-23 22:01:58 显示全部楼层
yqiaoi 发表于 2015-12-22 10:42
弱弱问一句:连接不上ftp.qt-project.org怎么办,  label一直显示正在连接

换个别的吧
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2015-12-28 22:52:56 显示全部楼层
QObject: Cannot create children for a parent that is in a different thread.
(Parent is Widget(0x75fe0c), parent's thread is QThread(0x146b3a10), current thread is QThread(0x1619e238)

您好  我在编译运行后使用软件  点击连接按钮会出现这样的错误  是为什么呢?
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2015-12-28 22:59:52 显示全部楼层
抱歉  楼上说的问题是 FTP(四)中的问题 是下载的文章结尾的代码
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2015-12-28 23:02:30 显示全部楼层
本帖最后由 Czach 于 2015-12-28 23:23 编辑

这盘文章给出的代码运行时也有这个问题

void Widget::on_connectButton_clicked()  
这个函数里的   ftp = new QFtp(this);
修改为  ftp = new QFtp();

可以让这一报错消失
可是这样程序运行貌似也不正确
连接FTP服务器连接不上貌似?
请问这个 this 是很重要的么?
去掉这个this之后点击连接感觉没有连接。。。
回复 支持 反对

使用道具 举报

累计签到:1564 天
连续签到:1 天
2015-12-29 08:08:30 显示全部楼层
Czach 发表于 2015-12-28 23:02
这盘文章给出的代码运行时也有这个问题

void Widget:n_connectButton_clicked()  

你用哪个Qt版本
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了