目录
commandFinished(int id, bool error)信号
简述
使用QT5实现了对QFtp类的使用,包括登录服务器,列出文件列表,返回上一级目录,下载文件,上传文件。
Qt5之后已经弃用QFtp类,要使用该类需要使用源码编译,我这里有已经编译好的包括QFtp类的工程,需要自取
通过网盘分享的文件:qt5ftp-master.rar
链接: https://pan.baidu.com/s/1WEb-Lu3in2roE3J6kYInDA?pwd=16jv
提取码: 16jv --来自百度网盘超级会员v6的分享
FTP服务器搭建方式
Win10开启与搭建FTP服务器(详细图文版)-CSDN博客
效果图
解决中文路径下载上传失败的问题
初始化
需要再.pro文件添加
QT += network
直接使用我的QFtp工程无需添加
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowTitle("FTP客户端");
// 设置表格不显示网格线
ui->tableWidget->setShowGrid(false);
// 自动调整列宽以填充整个视图
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 设置表格为不可编辑
ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
// 设置密码输入框的显示模式为密码模式
ui->lineEdit_pass->setEchoMode(QLineEdit::Password);
//未下载时先隐藏进度条
ui->progressBar->hide();
// 初始化当前目录为根目录
currentDirectory = "/";
// 将根目录路径添加到路径列表中
path << currentDirectory;
// 初始化FTP对象为空指针
ftp = nullptr;
// 设置按钮的背景图片,并调整大小
setButtonBackImage(ui->pushButton_back, ":/back.png", ui->pushButton_back->width() - 15, ui->pushButton_back->height() - 15);
setButtonBackImage(ui->pushButton_down, ":/download.png", ui->pushButton_down->width() - 15, ui->pushButton_down->height() - 15);
setButtonBackImage(ui->pushButton_up, ":/upload.png", ui->pushButton_up->width() - 15, ui->pushButton_up->height() - 15);
}
一.UI界面设计
所有控件(除掉label)从左到右控件名为
第一行 ,服务器地址:lineEdit_IP
用户名:lineEdit_Name
密码:lineEdit_pass
端口:lineEdit_port
连接:pushButton
第二行, 上传:pushButton_up
下载:pushButton_down
返回上一级:pushButton_back
当前路径:lineEdit_Path
调试窗口:textEdit
文件列表窗口:tableWidget
进度:progressBar
二.登录服务器
函数完整代码
// 登录槽按钮
void Dialog::on_pushButton_clicked()
{
if (!isConnect) {
// 创建一个新的QFtp对象
ftp = new QFtp(this);
// 连接到FTP服务器
ftp->connectToHost(ui->lineEdit_IP->text(), ui->lineEdit_port->text().toInt());
// 登录到FTP服务器
ftp->login(ui->lineEdit_Name->text(),ui->lineEdit_pass->text());
// 更新连接状态和按钮文本
isConnect = true;
ui->pushButton->setText("断开连接");
// 连接FTP的信号和槽函数
connect(ftp, &QFtp::commandFinished, this, &Dialog::ftpCommandFinished);
connect(ftp, &QFtp::commandStarted, this, &Dialog::ftpCommandStarted);
connect(ftp, &QFtp::listInfo, this, &Dialog::addToList);
connect(ftp, &QFtp::dataTransferProgress, this, &Dialog::updateDownloadProgress);
} else {
// 断开连接
isConnect = false;
ui->pushButton->setText("连接");
// 断开所有连接
disconnect(ftp, &QFtp::commandFinished, this, &Dialog::ftpCommandFinished);
disconnect(ftp, &QFtp::commandStarted, this, &Dialog::ftpCommandStarted);
disconnect(ftp, &QFtp::listInfo, this, &Dialog::addToList);
// 关闭FTP连接
ftp->close();
// 删除QFtp对象并释放内存
delete ftp;
ftp = nullptr;
}
}
在登录函数中,使用到了connectToHost 用来连接到服务器关于这个函数的解释如下图;简单来说就是不阻塞,异步执行,函数开始时发送commandStarted()信号,结束时发送commandFinished(int id, bool error)信号,函数两个参数,一个是服务器IP地址,一个是端口号默认是21.文件传输默认20。
再就是使用了login函数,该函数有两个重载,一个无参数,一个接受用户名和密码,具体使用看FTP服务器是否有限制。
commandStarted(int id)信号
QFtp类中有8个信号分别是
-
commandFinished(int id, bool error): 当一个命令执行完成时触发,传递命令的id以及一个指示是否发生错误的布尔值。
-
commandStarted(int id): 当一个命令开始执行时触发,传递命令的id。
-
dataTransferProgress(qint64 done, qint64 total): 当数据传输进度发生变化时触发,传递已完成的字节数和总字节数。
-
done(bool error): 当当前的操作(如上传、下载、删除等)完成时触发,传递一个指示是否发生错误的布尔值。
-
listInfo(const QUrlInfo &info): 当调用list()命令时,返回目录中的文件信息时触发,传递一个QUrlInfo对象,其中包含文件信息。
-
rawCommandReply(int replyCode, const QString &detail): 当使用rawCommand()方法发送原始FTP命令并收到回复时触发,传递回复码和详细信息。
-
readyRead(): 当有新的数据可供读取时触发。
-
stateChanged(int state): 当连接的状态发生变化时触发,传递一个表示新状态的整数值。
之前我们说了这个信号会在使用connectToHost时函数开始时会发送,那么这个信号是什么呢,我们看一下槽函数ftpCommandStarted的实现,可以看到在函数内部通过currentCommand()获取到了当前执行的命令的id。
connect(ftp, &QFtp::commandStarted, this, &Dialog::ftpCommandStarted);
// 根据信号类型实现不同操作
void Dialog::ftpCommandStarted(int)
{
// 获取当前命令的 ID
int id = ftp->currentCommand();
// 根据命令类型显示相应信息
switch (id) {
case QFtp::ConnectToHost:
showInfo(tr("正在连接到服务器"));
break;
case QFtp::Login:
showInfo(tr("正在登录"));
break;
case QFtp::Get:
showInfo(tr("正在下载"));
break;
case QFtp::Put:
showInfo(tr("正在上传"));
break;
case QFtp::Close:
showInfo(tr("正在关闭连接"));
break;
case QFtp::Cd:
showInfo(tr("正在更改目录"));
break;
}
}
commandFinished(int id, bool error)信号
这个信号呢会在函数执行完之后发送,如我们在连接服务器时,当连接登录服务器成功或失败时会调用。先不看其他部分,就看第一个和第二个if代码块,在我们登录成功或失败时,会打印相应的说明,如下图,
执行过程:点击连接按钮--》connectToHost函数发送commandstarted信号,信号ID为 QFtp::ConnectToHost,QFtp::Login --》登录成功或失败发送commandFinished信号,信号ID为 QFtp::ConnectToHost,QFtp::Login,带错误码-->>显示登录结果
connect(ftp, &QFtp::commandFinished, this, &Dialog::ftpCommandFinished);
ftpCommandFinished函数
void Dialog::ftpCommandFinished(int commandId, bool error)
{
if (ftp->currentCommand() == QFtp::ConnectToHost)
{
if (error)
showInfo(tr("连接服务器出现错误:")+ftp->errorString());
else
showInfo(tr("连接到服务器成功"));
}
else if (ftp->currentCommand() == QFtp::Login) {
if (error)
showInfo(tr("登录出现错误:")+ftp->errorString());
else
{
showInfo(tr("登录成功"));
// 更新文本框或标签的内容以显示服务器的根目录URL
ui->lineEdit_dir->setText("ftp://" + ui->lineEdit_IP->text()+"/");
ftp->list();
}
}
else if (ftp->currentCommand() == QFtp::Get) {
if (error) {
showInfo(tr("下载出现错误:")+ftp->errorString());
ui->progressBar->hide();
} else {
// 下载成功后将数据写入本地文件
localFile->write(ftp->readAll());
localFile->close(); // 关闭文件
showInfo(tr("下载成功"));
}
}
else if (ftp->currentCommand() == QFtp::Put) {
if (error) {
showInfo(tr("上传出现错误:")+ftp->errorString());
} else {
// 上传成功后将数据写入本地文件
localFile->write(ftp->readAll());
localFile->close(); // 关闭文件
showInfo(tr("上传成功"));
// 清空现有表格内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0);
//上传成功发送信号,重新刷新文件列表
ftp->list();
}
}
else if (ftp->currentCommand() == QFtp::Close)
{
showInfo(tr("断开服务器"));
}
else if (ftp->currentCommand() == QFtp::Cd) {
if (!error) {
//当前目录改变时,触发listInfo信号,刷新改变路径之后的文件列表
ftp->list();
} else {
// 打印错误信息
qDebug() << "错误: " << ftp->errorString();
}
}
else if (ftp->currentCommand() == QFtp::List) {
if (!error) {
//这里可以处理当前路径文件刷新完毕之后的处理
//每在一个路径调用list(),若在该路径下有多个文件将会触发多次listInfo信号,
//每触发一次,添加一个文件或文件夹到列表
} else {
qDebug() << "错误: " << ftp->errorString();
}
}
}
二.显示文件列表,
登录成功后在tableWidget上显示文件列表。在ftpCommandFinished函数中在我们收到登录成功时调用了ftp->list()函数,这个函数就是用来发送listInfo信号的,他会不断获取当前路径下的所有文件,每获取一次就再发送一次listInfo信号,直到获取完所有的文件,
connect(ftp, &QFtp::listInfo, this, &Dialog::addToList);
在addToList槽函数中,将每一次获取到的文件添加到tableWidget中,实现了一登陆就显示文件列表的操作。根据是否为文件夹添加相应图标,四列分别名称,类型,大小,日期。
// 向表格中添加项目
void Dialog::addToList(const QUrlInfo &urlInfo)
{
// 记录当前目录的文件数
fileListCount++;
// 创建新的行
int row = ui->tableWidget->rowCount();
ui->tableWidget->insertRow(row);
// 添加文件名到第一列
QTableWidgetItem *nameItem = new QTableWidgetItem(urlInfo.name());
// 检查项目是否为文件夹
if (urlInfo.isDir()) {
// 创建一个带有文件夹图标的 QIcon
QIcon folderIcon(":/folder.png"); // 文件夹图标资源路径需要自己项目目录结构进行调整
// 将文件夹图标设置为 QTableWidgetItem 的图标
nameItem->setIcon(folderIcon);
} else {
// 创建一个带有文件图标的 QIcon
QIcon fileIcon(":/file.png");
// 将文件图标设置为 QTableWidgetItem 的图标
nameItem->setIcon(fileIcon);
}
// 将带有文件名和图标的 QTableWidgetItem 添加到第一列
ui->tableWidget->setItem(row, 0, nameItem);
// 设置其他列的信息
QTableWidgetItem *typeItem = new QTableWidgetItem(urlInfo.isDir() ? "文件夹" : "文件");
ui->tableWidget->setItem(row, 1, typeItem);
QTableWidgetItem *sizeItem = new QTableWidgetItem(QString::number(urlInfo.size()));
ui->tableWidget->setItem(row, 2, sizeItem);
QTableWidgetItem *dateItem = new QTableWidgetItem(urlInfo.lastModified().toString());
ui->tableWidget->setItem(row, 3, dateItem);
if (urlInfo.isDir()) {
// 如果项目是文件夹,则设置相应的标志,允许用户选择项目 允许用户与该项目进行交互
nameItem->setFlags(nameItem->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
// 隐藏行号
ui->tableWidget->verticalHeader()->setVisible(false);
}
显示完文件列表了,再来实现点击文件夹进入文件夹
在ui界面鼠标右键点击tableWidget右键转到槽,选择itemClicked槽函数,只获取第一列的项的数据,其他列为文件属性,通过判断是否为文件夹,若是目录先清除当前目录下显示在tableWidget上的文件数据,调用了一个ftp->cd()函数,从函数名来看,功能显而易见,跳转路径。接受一个路径参数,我这里在当前路径直接调用从tablewidget上获取的文件夹名,使用相对路径直接进入文件夹;不是文件夹的话,记录当前点击的文件名,用于后续下载;
调用ftp->cd()函数还需要在ftpCommandFinished进行文件路径更改之后的操作,如显示文件列表如下面代码片段,我重新触发listInfo信号,获取当前文件列表。
else if (ftp->currentCommand() == QFtp::Cd) {
if (!error) {
//当前目录改变时,触发listInfo信号,刷新改变路径之后的文件列表
ftp->list();
} else {
// 打印错误信息
qDebug() << "错误: " << ftp->errorString();
}
}
//进入文件夹操作
void Dialog::on_tableWidget_itemClicked(QTableWidgetItem *item)
{
// 获取单击项的第一列文本和类型
QString itemName = ui->tableWidget->item(item->row(), 0)->text();
QString itemType = ui->tableWidget->item(item->row(), 1)->text();
ui->progressBar->hide();
// 检查是否为文件夹
bool isDir;
itemType == "文件夹"?isDir=true:isDir =false;
if (isDir) {
// 清空现有表格内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0);
//记录文件数置0
fileListCount = 0;
currentDirectoryTemp =itemName+"/";
// 更改FTP连接的当前目录
ftp->cd(itemName);
// 更新文本框或标签的内容以显示服务器的根目录URL
updateFolderListUI(currentDirectoryTemp,true);
}
else{
//如果是文件则记录当前鼠标左键点击的文件名,用于后面的文件下载。
filePath = itemName;
}
}
updateFolderListUI函数
我使用了QStringList path来记录当前文件路径,点击文件夹进入文件夹时,将文件夹名添加进去,再使用join拼接为一个字符串,表示当前路径,去掉末尾斜杠/。
void Dialog::updateFolderListUI(QString sur,bool isfront)
{
//isfront为true则是进入目录,否则为返回上一目录
if(isfront)
{
//将点击的项文本添加到绝对路径并且保存为当前路径
path.append(sur);
currentDirectory = path.join("");
currentDirectory= currentDirectory.left(currentDirectory.size()-1);
showInfo("当前绝对路径: "+currentDirectory);
// 更新文本框或标签的内容以显示服务器的根目录URL
ui->lineEdit_dir->setText("ftp://" + ui->lineEdit_IP->text()+ path.join(""));
}
else
{
//如果小于2则当前保留的唯一一个路径为根目录,就不允许返回上一级了
if(path.size()<2) return;
//否则删除最后一项,若绝对路经为/Data/Ttst/,删除最后一项为后的绝对路径为/Data/
path.removeLast();
currentDirectory = path.join("");
showInfo("当前绝对路径: "+path.join(""));
//如果当前路径为/即根目录时不操作直接更新,如返回后的路径/Data/ 不为/则删掉最后一个字符即/Data,
//为根目录时不操作
if(currentDirectory=="/")
;
else
currentDirectory= currentDirectory.left(currentDirectory.size()-1);
//更新UI显示路径
ui->lineEdit_dir->setText("ftp://" + ui->lineEdit_IP->text()+ path.join(""));
}
}
三.返回上一级目录
进入文件夹之后,再想返回到上一级目录就需要使用ftp->cd("../");在FTP协议中,"../" 表示上一级目录。
// 返回上一级目录按钮的点击槽函数
void Dialog::on_pushButton_back_clicked()
{
// 如果当前未连接到服务器,显示错误消息并返回
if (!ftp)
{
showInfo("未连接服务器");
return;
}
// 如果当前目录已经是根目录,无需进一步返回,直接返回
if (currentDirectory == "/")
return;
// 清空表格的内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0); // 清空表格的行数
// 更新界面显示为上一级目录
updateFolderListUI(currentDirectory, false);
// 请求FTP服务器返回上一级目录
ftp->cd("../");
}
四.下载文件
filePath即为tabwidget项点击时保存的文件名,使用get(const QString &file, QIODevice *dev, TransferType type) 第一个参数下载文件路径,第二个参数,输入输出设备,第三个参数传输类型默认是二进制传输,可以不传。下载成功或者失败会发送commandFinished信号进入这个代码块,将下载过来的数据写入本地文件。
else if (ftp->currentCommand() == QFtp::Get) {
if (error) {
showInfo(tr("下载出现错误:")+ftp->errorString());
ui->progressBar->hide();
} else {
// 下载成功后将数据写入本地文件
localFile->write(ftp->readAll());
localFile->close(); // 关闭文件
showInfo(tr("下载成功"));
}
}
需要注意的是,如果FTP服务器不是使用UTF-8的话,可以无法下载,或出现乱码现象。需要保证FTP服务器和Qt使用同一编码。上传文件也是同理
//下载文件
void Dialog::on_pushButton_down_clicked()
{
// 从FTP服务器下载文件
if (ftp) {
ui->progressBar->show();
// 指定要下载的文件名
QString remoteFilePath = currentDirectory + "/" + filePath;
// 打开文件对话框以选择下载路径
QString localFilePath = QFileDialog::getSaveFileName(this, tr("选择保存路径"), QDir::homePath() + "/" + filePath);
// 检查用户是否取消了文件对话框
if (localFilePath.isEmpty()) {
showInfo("下载已取消");
return;
}
// 创建一个本地文件对象
localFile = new QFile(localFilePath);
if (!localFile->open(QIODevice::WriteOnly)) {
// 文件无法打开,显示错误消息并返回
showInfo("无法打开本地文件:" + localFilePath);
return;
}
// 开始下载文件
ftp->get(remoteFilePath, localFile);
}
else {
showInfo("未连接服务器");
}
}
五.上传文件
进入服务器文件夹中,点击上传打开文件对话框选择要上传的文件,在调用put函数用于上传,同理,这个在函数结束时也会发送commandFinished信号,执行下面代码块,读取上传的文件,再写入服务器文件中。同时清空tabwidget所有文件数据,重新获取文件列表这时上传的文件成功获取。
else if (ftp->currentCommand() == QFtp::Put) {
if (error) {
showInfo(tr("上传出现错误:")+ftp->errorString());
} else {
// 上传成功后将数据写入服务器文件
localFile->write(ftp->readAll());
localFile->close(); // 关闭文件
showInfo(tr("上传成功"));
// 清空现有表格内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0);
//上传成功发送信号,重新刷新文件列表
ftp->list();
}
}
// 上传按钮的点击槽函数
void Dialog::on_pushButton_up_clicked()
{
// 检查是否连接到FTP服务器
if (!ftp)
{
showInfo("未连接到FTP服务器,请先连接");
return;
}
// 获取要上传的文件路径
QString localFilePath = QFileDialog::getOpenFileName(this, "选择要上传的文件", QDir::homePath());
// 如果路径为空,返回
if (localFilePath.isEmpty())
return;
// 从文件路径中提取文件名
QFileInfo fileInfo(localFilePath);
QString fileName = fileInfo.fileName();
// 构造FTP服务器上文件的路径
QString remoteFilePath = currentDirectory + "/" + fileName;
// 打开文件,并检查是否成功打开
localFile = new QFile(localFilePath);
if (!localFile->open(QIODevice::ReadOnly))
{
showInfo("无法打开本地文件:" + localFilePath);
return;
}
// 开始上传文件
ftp->put(localFile, remoteFilePath);
}
源码
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "qftp.h"
#include <QDebug>
#include <QTableWidget>
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowTitle("FTP客户端");
// 设置表格不显示网格线
ui->tableWidget->setShowGrid(false);
// 自动调整列宽以填充整个视图
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 设置表格为不可编辑
ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
// 设置密码输入框的显示模式为密码模式
ui->lineEdit_pass->setEchoMode(QLineEdit::Password);
//未下载时先隐藏进度条
ui->progressBar->hide();
// 初始化当前目录为根目录
currentDirectory = "/";
// 将根目录路径添加到路径列表中
path << currentDirectory;
// 初始化FTP对象为空指针
ftp = nullptr;
// 设置按钮的背景图片,并调整大小
setButtonBackImage(ui->pushButton_back, ":/back.png", ui->pushButton_back->width() - 15, ui->pushButton_back->height() - 15);
setButtonBackImage(ui->pushButton_down, ":/download.png", ui->pushButton_down->width() - 15, ui->pushButton_down->height() - 15);
setButtonBackImage(ui->pushButton_up, ":/upload.png", ui->pushButton_up->width() - 15, ui->pushButton_up->height() - 15);
}
/**
* @brief setButtonBackImage 设置按钮背景图片自适应
* @param button 按钮名
* @param image 图片路径
* @param sizeW 图片宽度
* @param sizeH 图片高度
*/
void Dialog::setButtonBackImage(QPushButton *button,QString image,int sizeW, int sizeH)
{
//163,163为原始分辨率,这里稍做了调整。
QPixmap pixmap(image);
QPixmap fitpixmap=pixmap.scaled(163,163).scaled(sizeW, sizeH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
button->setIcon(QIcon(fitpixmap));
button->setIconSize(QSize(sizeW,sizeH));
button->setFlat(true);//就是这句能够实现按钮透明,用png图片时很有用
button->setStyleSheet("border: 0px");//消除边框,取消点击效果
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::ftpCommandFinished(int commandId, bool error)
{
if (ftp->currentCommand() == QFtp::ConnectToHost)
{
if (error)
showInfo(tr("连接服务器出现错误:")+ftp->errorString());
else
showInfo(tr("连接到服务器成功"));
}
else if (ftp->currentCommand() == QFtp::Login) {
if (error)
showInfo(tr("登录出现错误:")+ftp->errorString());
else
{
showInfo(tr("登录成功"));
// 更新文本框或标签的内容以显示服务器的根目录URL
ui->lineEdit_dir->setText("ftp://" + ui->lineEdit_IP->text()+"/");
ftp->list();
}
}
else if (ftp->currentCommand() == QFtp::Get) {
if (error) {
showInfo(tr("下载出现错误:")+ftp->errorString());
ui->progressBar->hide();
} else {
// 下载成功后将数据写入本地文件
localFile->write(ftp->readAll());
localFile->close(); // 关闭文件
showInfo(tr("下载成功"));
}
}
else if (ftp->currentCommand() == QFtp::Put) {
if (error) {
showInfo(tr("上传出现错误:")+ftp->errorString());
} else {
// 上传成功后将数据写入本地文件
localFile->write(ftp->readAll());
localFile->close(); // 关闭文件
showInfo(tr("上传成功"));
// 清空现有表格内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0);
//上传成功发送信号,重新刷新文件列表
ftp->list();
}
}
else if (ftp->currentCommand() == QFtp::Close)
{
showInfo(tr("断开服务器"));
}
else if (ftp->currentCommand() == QFtp::Cd) {
if (!error) {
//当前目录改变时,触发listInfo信号,刷新改变路径之后的文件列表
ftp->list();
} else {
// 打印错误信息
qDebug() << "错误: " << ftp->errorString();
}
}
else if (ftp->currentCommand() == QFtp::List) {
if (!error) {
//这里可以处理当前路径文件刷新完毕之后的处理
//每在一个路径调用list(),若在该路径下有多个文件将会触发多次listInfo信号,
//每触发一次,添加一个文件或文件夹到列表
} else {
qDebug() << "错误: " << ftp->errorString();
}
}
}
void Dialog::updateFolderListUI(QString sur,bool isfront)
{
//isfront为true则是进入目录,否则为返回上一目录
if(isfront)
{
//将点击的项文本添加到绝对路径并且保存为当前路径
path.append(sur);
currentDirectory = path.join("");
currentDirectory= currentDirectory.left(currentDirectory.size()-1);
showInfo("当前绝对路径: "+currentDirectory);
// 更新文本框或标签的内容以显示服务器的根目录URL
ui->lineEdit_dir->setText("ftp://" + ui->lineEdit_IP->text()+ path.join(""));
}
else
{
//如果小于2则当前保留的唯一一个路径为根目录,就不允许返回上一级了
if(path.size()<2) return;
//否则删除最后一项,若绝对路经为/Data/Ttst/,删除最后一项为后的绝对路径为/Data/
path.removeLast();
currentDirectory = path.join("");
showInfo("当前绝对路径: "+path.join(""));
//如果当前路径为/即根目录时不操作直接更新,如返回后的路径/Data/ 不为/则删掉最后一个字符即/Data,
//为根目录时不操作
if(currentDirectory=="/")
;
else
currentDirectory= currentDirectory.left(currentDirectory.size()-1);
//更新UI显示路径
ui->lineEdit_dir->setText("ftp://" + ui->lineEdit_IP->text()+ path.join(""));
}
}
// 当 QFtp 对象开始执行一个命令时,会发出 commandStarted 信号。
// 根据信号类型实现不同操作
void Dialog::ftpCommandStarted(int)
{
// 获取当前命令的 ID
int id = ftp->currentCommand();
// 根据命令类型显示相应信息
switch (id) {
case QFtp::ConnectToHost:
showInfo(tr("正在连接到服务器"));
break;
case QFtp::Login:
showInfo(tr("正在登录"));
break;
case QFtp::Get:
showInfo(tr("正在下载"));
break;
case QFtp::Put:
showInfo(tr("正在上传"));
break;
case QFtp::Close:
showInfo(tr("正在关闭连接"));
break;
case QFtp::Cd:
showInfo(tr("正在更改目录"));
break;
}
}
// 登录槽按钮
void Dialog::on_pushButton_clicked()
{
if (!isConnect) {
// 创建一个新的QFtp对象
ftp = new QFtp(this);
// 连接到FTP服务器
ftp->connectToHost(ui->lineEdit_IP->text(), ui->lineEdit_port->text().toInt());
// 登录到FTP服务器
ftp->login();
// 更新连接状态和按钮文本
isConnect = true;
ui->pushButton->setText("断开连接");
// 连接FTP的信号和槽函数
connect(ftp, &QFtp::commandFinished, this, &Dialog::ftpCommandFinished);
connect(ftp, &QFtp::commandStarted, this, &Dialog::ftpCommandStarted);
connect(ftp, &QFtp::listInfo, this, &Dialog::addToList);
connect(ftp, &QFtp::dataTransferProgress, this, &Dialog::updateDownloadProgress);
} else {
// 断开连接
isConnect = false;
ui->pushButton->setText("连接");
// 断开所有连接
disconnect(ftp, &QFtp::commandFinished, this, &Dialog::ftpCommandFinished);
disconnect(ftp, &QFtp::commandStarted, this, &Dialog::ftpCommandStarted);
disconnect(ftp, &QFtp::listInfo, this, &Dialog::addToList);
// 关闭FTP连接
ftp->close();
// 删除QFtp对象并释放内存
delete ftp;
ftp = nullptr;
}
}
// 向表格中添加项目
void Dialog::addToList(const QUrlInfo &urlInfo)
{
// 记录当前目录的文件数
fileListCount++;
// 创建新的行
int row = ui->tableWidget->rowCount();
ui->tableWidget->insertRow(row);
// 添加文件名到第一列
QTableWidgetItem *nameItem = new QTableWidgetItem(urlInfo.name());
// 检查项目是否为文件夹
if (urlInfo.isDir()) {
// 创建一个带有文件夹图标的 QIcon
QIcon folderIcon(":/folder.png"); // 文件夹图标资源路径需要自己项目目录结构进行调整
// 将文件夹图标设置为 QTableWidgetItem 的图标
nameItem->setIcon(folderIcon);
} else {
// 创建一个带有文件图标的 QIcon
QIcon fileIcon(":/file.png");
// 将文件图标设置为 QTableWidgetItem 的图标
nameItem->setIcon(fileIcon);
}
// 将带有文件名和图标的 QTableWidgetItem 添加到第一列
ui->tableWidget->setItem(row, 0, nameItem);
// 设置其他列的信息
QTableWidgetItem *typeItem = new QTableWidgetItem(urlInfo.isDir() ? "文件夹" : "文件");
ui->tableWidget->setItem(row, 1, typeItem);
QTableWidgetItem *sizeItem = new QTableWidgetItem(QString::number(urlInfo.size()));
ui->tableWidget->setItem(row, 2, sizeItem);
QTableWidgetItem *dateItem = new QTableWidgetItem(urlInfo.lastModified().toString());
ui->tableWidget->setItem(row, 3, dateItem);
if (urlInfo.isDir()) {
// 如果项目是文件夹,则设置相应的标志,允许用户选择项目 允许用户与该项目进行交互
nameItem->setFlags(nameItem->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
// 隐藏行号
ui->tableWidget->verticalHeader()->setVisible(false);
}
//进入文件夹操作
void Dialog::on_tableWidget_itemClicked(QTableWidgetItem *item)
{
// 获取单击项的第一列文本和类型
QString itemName = ui->tableWidget->item(item->row(), 0)->text();
QString itemType = ui->tableWidget->item(item->row(), 1)->text();
ui->progressBar->hide();
// 检查是否为文件夹
bool isDir;
itemType == "文件夹"?isDir=true:isDir =false;
if (isDir) {
// 清空现有表格内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0);
//记录文件数置0
fileListCount = 0;
currentDirectoryTemp =itemName+"/";
// 更改FTP连接的当前目录
ftp->cd(itemName);
// 更新文本框或标签的内容以显示服务器的根目录URL
updateFolderListUI(currentDirectoryTemp,true);
}
else{
//如果是文件则记录当前鼠标左键点击的文件名,用于后面的文件下载。
filePath = itemName;
}
}
// 返回上一级目录按钮的点击槽函数
void Dialog::on_pushButton_back_clicked()
{
// 如果当前未连接到服务器,显示错误消息并返回
if (!ftp)
{
showInfo("未连接服务器");
return;
}
// 如果当前目录已经是根目录,无需进一步返回,直接返回
if (currentDirectory == "/")
return;
// 清空表格的内容
ui->tableWidget->clearContents();
ui->tableWidget->setRowCount(0); // 清空表格的行数
// 更新界面显示为上一级目录
updateFolderListUI(currentDirectory, false);
// 请求FTP服务器返回上一级目录
ftp->cd("../");
}
// 上传按钮的点击槽函数
void Dialog::on_pushButton_up_clicked()
{
// 检查是否连接到FTP服务器
if (!ftp)
{
showInfo("未连接到FTP服务器,请先连接");
return;
}
// 获取要上传的文件路径
QString localFilePath = QFileDialog::getOpenFileName(this, "选择要上传的文件", QDir::homePath());
// 如果路径为空,返回
if (localFilePath.isEmpty())
return;
// 从文件路径中提取文件名
QFileInfo fileInfo(localFilePath);
QString fileName = fileInfo.fileName();
// 构造远程服务器上文件的路径
QString remoteFilePath = currentDirectory + "/" + fileName;
// 打开文件,并检查是否成功打开
localFile = new QFile(localFilePath);
if (!localFile->open(QIODevice::ReadOnly))
{
showInfo("无法打开本地文件:" + localFilePath);
return;
}
// 开始上传文件
ftp->put(localFile, remoteFilePath);
}
//下载文件
void Dialog::on_pushButton_down_clicked()
{
// 从FTP服务器下载文件
if (ftp) {
ui->progressBar->show();
// 指定要下载的文件名
QString remoteFilePath = currentDirectory + "/" + filePath;
// 打开文件对话框以选择下载路径
QString localFilePath = QFileDialog::getSaveFileName(this, tr("选择保存路径"), QDir::homePath() + "/" + filePath);
// 检查用户是否取消了文件对话框
if (localFilePath.isEmpty()) {
showInfo("下载已取消");
return;
}
// 创建一个本地文件对象
localFile = new QFile(localFilePath);
if (!localFile->open(QIODevice::WriteOnly)) {
// 文件无法打开,显示错误消息并返回
showInfo("无法打开本地文件:" + localFilePath);
return;
}
// 开始下载文件
ftp->get(remoteFilePath, localFile);
}
else {
showInfo("未连接服务器");
}
}
// 定义一个槽函数来更新下载进度条
void Dialog::updateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if (bytesTotal > 0) {
int progress = (bytesReceived * 100) / bytesTotal;
ui->progressBar->setValue(progress);
}
}
//调试函数
void Dialog::showInfo(QString info)
{
ui->textEdit->append("["+QTime::currentTime().toString()+"]"+" "+info);
}
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
namespace Ui {
class Dialog;
}
class QUrlInfo;
class QFtp;
class QTableWidgetItem ;
class QFile ;
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
void on_tableWidget_itemClicked(QTableWidgetItem *item);
void on_pushButton_back_clicked();
void on_pushButton_up_clicked();
void on_pushButton_down_clicked();
void ftpCommandFinished(int commandId, bool error);
void ftpCommandStarted(int);
void addToList(const QUrlInfo &urlInfo);
void updateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private:
Ui::Dialog *ui;
QFtp *ftp;
bool isConnect = false;
QString currentDirectory,currentDirectoryTemp;
QStringList path;
int fileListCount = 0;
QString filePath;
QFile *localFile;
void setButtonBackImage(QPushButton *button,QString image,int sizeW, int sizeH);
void updateFolderListUI(QString,bool);
void showInfo(QString info);
};
#endif // DIALOG_H