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::Unconnected | 0 | 没有连接到主机 |
QFtp::HostLookup | 1 | 正在进行主机名查找 |
QFtp::Connecting | 2 | 正在尝试连接到主机 |
QFtp::Connected | 3 | 已实现与主机的连接 |
QFtp::LoggedIn | 4 | 已实现连接和用户登录 |
QFtp::Closing | 5 | 连接正在关闭,但尚未关闭(当连接关闭时,状态将为 Unconnected) |
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::None | 0 | 未执行任何命令 |
QFtp::SetTransferMode | 1 | 设置传输模式 |
QFtp::SetProxy | 2 | 切换代理打开或关闭 |
QFtp::ConnectToHost | 3 | 正在执行 connectToHost() |
QFtp::Login | 4 | 正在执行 login() |
QFtp::Close | 5 | 正在执行 close() |
QFtp::List | 6 | 正在执行 list() |
QFtp::Cd | 7 | 正在执行 cd() |
QFtp::Get | 8 | 正在执行 get() |
QFtp::Put | 9 | 正在执行 put() |
QFtp::Remove | 10 | 正在执行 remove() |
QFtp::Mkdir | 11 | 正在执行 mkdir() |
QFtp::Rmdir | 12 | 正在执行 rmdir() |
QFtp::Rename | 13 | 正在执行 rename() |
QFtp::RawCommand | 14 | 正在执行 rawCommand() |
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::NoError | 0 | 没有发生错误 |
QFtp::HostNotFound | 2 | 主机名查找失败 |
QFtp::ConnectionRefused | 3 | 服务器拒绝连接 |
QFtp::NotConnected | 4 | 尝试发送命令,但没有到服务器的连接 |
QFtp::UnknownError | 1 | 除了以上指定的错误发生 |
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::Passive | 1 | 客户端连接到服务器以传输其数据 |
QFtp::Active | 0 | 服务器连接到客户端以传输其数据 |
数据传输类型
枚举 QFtp::TransferType:此枚举标识使用 get 和 put 命令进行数据传输的类型。
常量 | 值 | 描述 |
---|---|---|
QFtp::Binary | 0 | 数据将以二进制模式传输 |
QFtp::Ascii | 1 | 数据将以 Ascii 模式传输,换行符将转换为本地格式 |
相关文章推荐
- QT学习 第一章:基本对话框
- 使用Shiboken为C++和Qt库创建Python绑定
- Qt 5.6更新至RC版,最终版本近在咫尺
- Qt定时器和随机数详解
- Qt实现图片移动实例(图文教程)
- Qt for Android开发实例教程
- QModelIndex/Role/Model介紹<二>
- Qt Model/View/Delegate浅谈 - QAbstractListModel
- Qt Model/View/Delegate浅谈 - roleNames()
- QT历程(一):与CryptoJs对应的AES加密
- 基于PyQt5的快速开发框架QFramer
- OSX下安装PyQt
- ok6410开发板移植DirectFB手记
- 【笔记】给Qt内嵌一个Chrome吧
- 【算法】最短路径之A*搜索
- qt入门必备
- 在 Qt4 中使用 C++11
- Hello Word ~ v0.2.2 背单词软件发布 -- By WHYPRO
- ubuntu下opencv和qt的安装配置
- linux下opengl的安装(with qt)