Qt之使用QFtp实现登录,上传,下载,代码每一句都有注释

目录

简述

效果图

初始化

一.UI界面设计

二.登录服务器

commandStarted(int id)信号

commandFinished(int id, bool error)信号

二.显示文件列表,

三.返回上一级目录

四.下载文件

五.上传文件


简述

        使用QT5实现了对QFtp类的使用,包括登录服务器,列出文件列表,返回上一级目录,下载文件,上传文件。

        Qt5之后已经弃用QFtp类,要使用该类需要使用源码编译,我这里有已经编译好的包括QFtp类的工程,需要自取

链接:https://pan.baidu.com/s/1nQPxHP1l--x8RJ3W6C4qcg 
提取码:43ad 
 

效果图

解决中文路径下载上传失败的问题

初始化

     需要再.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个信号分别是

  1. commandFinished(int id, bool error): 当一个命令执行完成时触发,传递命令的id以及一个指示是否发生错误的布尔值。

  2. commandStarted(int id): 当一个命令开始执行时触发,传递命令的id。

  3. dataTransferProgress(qint64 done, qint64 total): 当数据传输进度发生变化时触发,传递已完成的字节数和总字节数。

  4. done(bool error): 当当前的操作(如上传、下载、删除等)完成时触发,传递一个指示是否发生错误的布尔值。

  5. listInfo(const QUrlInfo &info): 当调用list()命令时,返回目录中的文件信息时触发,传递一个QUrlInfo对象,其中包含文件信息。

  6. rawCommandReply(int replyCode, const QString &detail): 当使用rawCommand()方法发送原始FTP命令并收到回复时触发,传递回复码和详细信息。

  7. readyRead(): 当有新的数据可供读取时触发。

  8. 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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值