您的位置:首页 > 编程语言 > Qt开发

Qt之QFtp

2016-11-25 18:30 169 查看

简述

QFtp 类提供了一个 FTP 协议的客户端实现。

该类提供了一个到 FTP 的直接接口,允许对请求有更多的控制。但是,对于新的应用程序,建议使用 QNetworkAccessManager 和 QNetworkReply,因为这些类拥有一个更简单、还更强大的 API。

简述

QFtp

工作流程

基本使用
连接并登录 FTP 服务器

切换工作目录

列出目录中的内容

创建文件夹

删除文件目录
删除文件

删除目录

重命名

上传文件

下载文件

连接状态

获取当前命令

错误处理

文件传输模式

数据传输类型

QFtp

QFtp 异步工作,因此没有阻塞函数。如果无法立即执行操作,函数仍将立即返回,并且该操作将被调度以供以后执行。调度操作的结果通过信号报告,这种方法依赖于事件循环操作。

可以调度的操作(也被称为“命令”)有:connectToHost()、login()、close()、list()、cd()、get()、put()、remove()、mkdir()、rmdir()、rename() 和 rawCommand()。

所有这些命令都会返回一个唯一的标识符,允许跟踪当前正在执行的命令。当命令的执行开始时,发出带有命令标识符的 commandStarted() 信号。当命令完成时,会发出 commandFinished() 信号,并带有命令标识符和一个 bool 参数,表明该命令在完成时是否出错。

在某些情况下,可能想要执行一系列命令。例如,如果要连接并登录到 FTP 服务器,简单的实现如下:

QFtp *ftp = new QFtp(parent);
ftp->connectToHost("192.168.***.***", 21);
ftp->login("wang", "123456");


在这种情况下,调度了两个 FTP 命令。当最后一个调度命令完成时,会发出 done() 信号,并带有一个 bool 参数,告诉你序列在完成时是否出错。

如果命令序列中的某个命令的执行期间发生错误,则所有挂起的命令(即:已调度,但尚未执行的命令)会被清除,并且不为它们发射信号。

一些命令,例如 list() ,会发出额外的信号(listInfo())以报告其结果。

对于文件传输,QFtp 可以使用主动或被动模式,并且默认使用被动文件传输模式,可使用 setTransferMode() 设置。

调用 setProxy() 使 QFtp 通过 FTP 代理服务器连接。

函数 hasPendingCommands() 和 clearPendingCommands() 允许查询和清除挂起的命令列表。

如果你在网络方便比较有经验,想要有完整的控制,可以使用 rawCommand() 来执行任意 FTP 命令。

警告:当前版本的 QFtp 不完全支持非 Unix FTP 服务器。

工作流程

如果要从 FTP 服务器下载 /home/wang/ftp.qdoc 文件,可以分为下面几步:

ftp->connectToHost("192.168.***.***", 21);     // id == 1
ftp->login("wang", "123456");                  // id == 2
ftp->cd("/home/wang");                         // id == 3
ftp->get("ftp.qdoc");                          // id == 4
ftp->close();                                  // id == 5


流程如下:

connectToHost() - 指定主机和端口号,连接 FTP 服务器

login() - 指定用户名和密码,登录到 FTP 服务器

cd() - 改变服务器的工作目录为 /etc(类似于 Linux 下的命令行:cd /home/wang)

get() - 从服务器上下载文件 passwd(绝对路径为:/home/wang/ftp.qdoc)

close() - 关闭到 FTP 服务器的连接

对于该示例,发射以下序列的信号:

commandStarted(1)
stateChanged(HostLookup)
stateChanged(Connecting)
stateChanged(Connected)
commandFinished(1, false)

commandStarted(2)
stateChanged(LoggedIn)
commandFinished(2, false)

commandStarted(3)
commandFinished(3, false)

commandStarted(4)
dataTransferProgress(0, 8710)
dataTransferProgress(8192, 8710)
readyRead()
dataTransferProgress(8710, 8710)
readyRead()
commandFinished(4, false)

commandStarted(5)
stateChanged(Closing)
stateChanged(Unconnected)
commandFinished(5, false)

done(false)


如果要显示进度条以通知用户下载进度,上述示例中的 dataTransferProgress() 信号就会很有用。readyRead() 信号告诉你有数据准备好被读取,然后可以使用 bytesAvailable() 函数查询数据量,并且可以使用 read() 或 readAll() 函数读取数据量。

如果上述示例登录失败(例如:用户名/密码错误),信号将如下所示:

commandStarted(1)
stateChanged(HostLookup)
stateChanged(Connecting)
stateChanged(Connected)
commandFinished(1, false)

commandStarted(2)
commandFinished(2, true)

done(true)


然后,可以使用 error() 和 errorString() 函数获取有关错误的详细信息。

基本使用

连接并登录 FTP 服务器

在进行任何操作之前,首先确保有可用的 FTP 服务器,并存在有效的用户(例如:wang),然后连接并登录到 FTP 服务器。

QFtp *ftp = new QFtp(parent);
ftp->connectToHost("192.168.***.***", 21);  // 主机:192.168.***.*** 端口号:21
ftp->login("wang", "123456");  // 用户名:wang 密码:123456


切换工作目录

登录成功之后,默认情况下,服务器的工作目录为用户主目录(例如:/home/wang),可以使用 cd() 来进行更改。例如,切换工作目录为 /home/wang/doc:

ftp->cd("/home/wang/doc");


在进行其他命令操作之前,先一起看看 doc 的树结构。

[wang@localhost doc]$ pwd
/home/wang/doc
[wang@localhost doc]$ tree
.
├── c++
│   └── qt5_cadaques.pdf
├── hello.sh
├── linux
│   └── linux-program.pdf
└── python
└── hello.py

3 directories, 4 files


里面包含 3 个目录以及 4 个文件。

列出目录中的内容

要列出 dir 目录的内容,可以使用 list(),如果 dir 为空,将列出当前目录的内容。

int QFtp::list(const QString & dir = QString())

对于找到的每个目录条目,都会发出 listInfo() 信号。

// 输出文件详细信息
connect(ftp, &QFtp::listInfo, [=](const QUrlInfo &urlInfo) {
qDebug() << urlInfo.name() << urlInfo.size() << urlInfo.owner() << urlInfo.group()
<< urlInfo.lastModified().toString("MMM dd yyyy") << urlInfo.isDir();
});
ftp->list();


注意:这里只列出文件的一部分信息,其他更多信息请参考 QUrlInfo 。

输出如下:

“c++” 29 “1000” “1000” “十一月 28 2016” true

“hello.sh” 55 “1000” “1000” “十月 20 2016” false

“linux” 30 “1000” “1000” “十一月 28 2016” true

“python” 21 “1000” “1000” “十一月 28 2016” true

可以和服务端比对一下:

[wang@localhost doc]$ ls -l
总用量 4
drwxrwxr-x. 2 wang wang 29 11月 28 10:41 c++
-rw-rw-r--. 1 wang wang 55 10月 20 15:59 hello.sh
drwxrwxr-x. 2 wang wang 30 11月 28 10:40 linux
drwxrwxr-x. 2 wang wang 21 11月 28 10:39 python


创建文件夹

要在服务器上创建一个名为 dir 的目录,使用 mkdir()。

ftp->mkdir("new_dir");


创建完成之后,去服务端验证一下吧!

[wang@localhost doc]$ ls -l | grep new*
drwxr-xr-x  2 wang wang  6 11月 28 11:05 new_dir


显然,目录 new_dir 已经创建完成。

删除文件/目录

有创建必然会有删除,没错!remove() 是删除文件,rmdir() 则是删除目录。

删除文件

要从服务器中删除名为 file 的文件,使用 remove()。

ftp->remove("hello.sh");  // 删除文件


删除前:

[wang@localhost doc]$ ls
c++  hello.sh  linux  new_dir  python


删除后:

[wang@localhost doc]$ ls
c++  linux  new_dir  python


删除目录

要从服务器中删除名为 dir 的目录,使用 rmdir()。

ftp->rmdir("new_dir");  // 删除空目录
ftp->rmdir("c++");  // 删除非空目录


删除前:

[wang@localhost doc]$ tree
.
├── c++
│   └── qt5_cadaques.pdf
├── linux
│   └── linux-program.pdf
├── new_dir
└── python
└── hello.py

4 directories, 3 files


删除后:

[wang@localhost doc]$ tree
.
├── c++
│   └── qt5_cadaques.pdf
├── linux
│   └── linux-program.pdf
└── python
└── hello.py

3 directories, 3 files


显然,只能删除空目录。如果目录下有文件,则不能删除。

重命名

如果要对文件进行重命名,使用 rename()。

ftp->rename("c++", "c");  // c++ -> c


重命名前:

[wang@localhost doc]$ ls
c++  linux  python


重命名后:

[wang@localhost doc]$ ls
c  linux  python


上传文件

关于上传文件,有两个重载的函数:

int QFtp::put(QIODevice * dev, const QString & file, TransferType type = Binary)

从 IO 设备 dev 读取数据,并将其写入服务器上名为 file 的文件。从 IO 设备读取数据块,因此此重载允许传输大量数据,而无需立即将所有数据读入内存。

注意:确保 dev 指针在操作期间有效(在发出 commandFinished() 时可以安全地删除它)

m_file = new QFile("E:/Qt.zip");
ftp->put(m_file, "Qt.zip");


上传完成后,去服务端查看。

[wang@localhost doc]$ ls -al | grep Qt*
-rw-r--r--   1 wang wang 54246299 11月 28 11:43 Qt.zip


int QFtp::put(const QByteArray & data, const QString & file, TransferType type = Binary)

将给定数据的副本写入服务器上名为 file 的文件。

ftp->put("Hello World!\nI'am a Qter.", "readMe.txt");


上传完成后,去服务端查看。

[wang@localhost doc]$ ls
c  linux  python  readMe.txt
[wang@localhost doc]$ cat readMe.txt
Hello World!
I'am a Qter.
[wang@localhost doc]$


如果要获取上传的进度,可以关联 dataTransferProgress() 信号。

下载文件

要从服务器下载文件,使用 get()。

int QFtp::get(const QString & file, QIODevice * dev = 0, TransferType type = Binary)

如果 dev 为 0,则当有可用的数据可读时,发出 readyRead() 信号。然后,可以使用 read() 或 readAll() 函数读取数据。

如果 dev 不为 0,则将数据直接写入设备 dev。确保 dev 指针在操作期间有效(在发出commandFinished() 信号时可以安全地删除它)。在这种情况下,readyRead() 信号不发出,你不能用read() 或 readAll() 函数读取数据。

如果你不立即读取数据,它变得可用,即当 readyRead() 信号被发出时,它仍然可用,直到下一个命令开始。

例如,如果要在有可用的数据时向用户提供数据,请连接到 readyRead() 信号并立即读取数据。另一方面,如果只想使用完整的数据,则可以连接到 commandFinished() 信号,并在 get() 命令完成后读取数据。

m_file = new QFile("E:/Qt.zip");
if (!m_file->open(QIODevice::WriteOnly)) {
m_file->remove();
delete m_file;
m_file = NULL;
} else {
ftp->get("Qt.zip", m_file);  // 下载文件
}


注意: 很多人下载文件失败,是由于文件 open() 了,但是在下载完之后,没有 close()。加上
ftp->get()
是异步的,也不能在调用之后立马将文件 close()。所以,正确的做法应该是连接 QFtp 的
commandFinished(int,bool)
信号,在槽函数中进行:

void FtpWindow::commandFinished(int id, bool error)
{
Q_UNUSED(id);

QFtp::Command command = ftp->currentCommand();
switch (command) {
case QFtp::Get: {
if (error) {
m_file->close();
m_file->remove();
} else {
m_file->close();
}
delete m_file;
break;
}
default:
break;
}
}


连接状态

当前的状态 QFtp::State 由 state() 返回 ,当状态改变时,发出 stateChanged() 信号,参数是连接的新状态。该信号通常用于 connectToHost() 或者 close() 命令,也可以“自发地”发射,例如:当服务器意外关闭连接时。

常量描述
QFtp::Unconnected0没有连接到主机
QFtp::HostLookup1正在进行主机名查找
QFtp::Connecting2正在尝试连接到主机
QFtp::Connected3已实现与主机的连接
QFtp::LoggedIn4已实现连接和用户登录
QFtp::Closing5连接正在关闭,但尚未关闭(当连接关闭时,状态将为 Unconnected)
例如,当连接 TCP 服务器的时候,使用一个 QLabel 显示连接的状态信息。

void FtpWindow::stateChanged(int state)
{
switch (state) {
case QFtp::Unconnected: {
stateLabel->setText(QStringLiteral("没有连接到主机"));
break;
}
case QFtp::HostLookup: {
stateLabel->setText(QStringLiteral("正在进行主机名查找"));
break;
}
case QFtp::Connecting: {
stateLabel->setText(QStringLiteral("正在尝试连接到主机"));
break;
}
case QFtp::Connected: {
stateLabel->setText(QStringLiteral("已实现与主机的连接"));
break;
}
case QFtp::LoggedIn: {
stateLabel->setText(QStringLiteral("已实现连接和用户登录"));
break;
}
case QFtp::Closing: {
stateLabel->setText(QStringLiteral("连接正在关闭"));
break;
}
default:
break;
}
}


获取当前命令

currentId() 和 currentCommand() 提供了有关当前执行命令。currentCommand() 返回当前 FTP 的命令类型 QFtp::Command,如果没有命令正在执行,则返回 None。

常量描述
QFtp::None0未执行任何命令
QFtp::SetTransferMode1设置传输模式
QFtp::SetProxy2切换代理打开或关闭
QFtp::ConnectToHost3正在执行 connectToHost()
QFtp::Login4正在执行 login()
QFtp::Close5正在执行 close()
QFtp::List6正在执行 list()
QFtp::Cd7正在执行 cd()
QFtp::Get8正在执行 get()
QFtp::Put9正在执行 put()
QFtp::Remove10正在执行 remove()
QFtp::Mkdir11正在执行 mkdir()
QFtp::Rmdir12正在执行 rmdir()
QFtp::Rename13正在执行 rename()
QFtp::RawCommand14正在执行 rawCommand()
这允许你对特定命令执行特定操作。例如,在 FTP 客户端中,可能需要在启动 list() 命令时清除目录视图。在这种情况下,可以简单地核查在连接到 commandStarted() 信号的槽函数中 currentCommand() 是否为 List。

void FtpWindow::commandStarted(int id)
{
QFtp::Command command = ftp->currentCommand();
switch (command) {
case QFtp::List: {  // 正在执行 list() - 列出目录下的文件
// 清除目录视图 QTreeWidget
fileListTree->clear();
break;
}
default:
break;
}

qDebug() << "commandStarted " << id;
}


错误处理

通过 error() 和 errorString() 返回最后一次发生的错误。当接收到 commandFinished() 或者 done() 信号时,如果标识 error 的 bool 参数 为 true,这就非常有用了。

error() 返回的是一个 QFtp::Error 枚举类型,用来标识发生的错误。

常量描述
QFtp::NoError0没有发生错误
QFtp::HostNotFound2主机名查找失败
QFtp::ConnectionRefused3服务器拒绝连接
QFtp::NotConnected4尝试发送命令,但没有到服务器的连接
QFtp::UnknownError1除了以上指定的错误发生
注意:如果启动一个新命令,错误的状态会被重置为 NoError。

errorString() 返回的是一个人类可读的字符串。通常是(但不总是)来自服务器的回复,因此并不总是可以翻译字符串。如果消息来自 Qt,则字符串已经通过 tr()。

void FtpWindow::commandFinished(int id, bool error)
{
Q_UNUSED(id);

QFtp::Command command = ftp->currentCommand();
switch (command) {
case QFtp::ConnectToHost: {  // 连接 FTP 服务器
if (error) { // 发生错误
qDebug() << "Error " << ftp->error() << "ErrorString " << ftp->errorString();
QMessageBox::information(this, "FTP", QStringLiteral("无法连接到 FTP 服务器,请检查主机名是否正确!"));

ftp->abort();
ftp->deleteLater();
ftp = NULL;
} else {
qDebug() << QStringLiteral("登录 FTP 服务器");
}
break;
}
default:
break;
}
}


文件传输模式

枚举 QFtp::TransferMode:

FTP 使用两个套接字连接:一个用于命令,另一个用于发送数据。 虽然命令连接始终由客户端发起,但第二个连接可以由客户端或服务器发起。

此枚举定义客户端(被动模式)还是服务器(活动模式)应设置数据连接。

常量描述
QFtp::Passive1客户端连接到服务器以传输其数据
QFtp::Active0服务器连接到客户端以传输其数据

数据传输类型

枚举 QFtp::TransferType:

此枚举标识使用 get 和 put 命令进行数据传输的类型。

常量描述
QFtp::Binary0数据将以二进制模式传输
QFtp::Ascii1数据将以 Ascii 模式传输,换行符将转换为本地格式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息