第34篇 网络(四)FTP(二)

news2024/11/28 0:38:43

导语

前面讲述了一个最简单的FTP客户端程序的编写,这一节我们将这个程序进行扩展,使其可以浏览并能下载服务器上的所有文件。

环境:Windows Xp + Qt 4.8.5+QtCreator 2.8.0

目录

  • 一、修改界面
  • 二、功能实现

正文

一、修改界面

我们删除了TextBrowser ,加入了几个LabelLine EditPush Button部件,一个Tree Widget及一个Progress Bar部件。然后我们对其中几个部件做如下更改。

(1)将“FTP服务器”标签后的Line EditobjectName属性改为ftpServerLineEdit,其text 属性改为ftp.qt-project.org

(2)将“用户名”标签后的Line EditobjectName属性改为userNameLineEdit,其text属性改为anonymous,将其toolTip属性改为“默认用户名请使用:anonymous ,此时密码任意。”

(3)将“密码”标签后的Line EditobjectName属性改为passWordLineEdit,其text属性改为123456,将其echoMode属性改为Password

(4)将“连接”按钮的objectName属性改为connectButton

(5)将“返回上一级目录”按钮的objectName属性改为cdToParentButton

(6)将“下载”按钮的objectName属性改为downloadButton

(7)将Tree WidgetobjectName属性改为fileList,然后在Tree Widget部件上单击鼠标右键,选择Edit Items菜单,添加列属性如下。

最终界面如图所示:

下面我们的程序中,就是实现在用户填写完相关信息后,按下“连接”按钮,就可以连接到FTP服务器,并在TreeWidget中显示服务器上的所有文件,我们可以按下“下载”按钮来下载选中的文件,并使用进度条显示下载进度。

二、功能实现

1.更改widget.h文件。

(1)添加头文件#include <QtGui>

(2)在private中添加变量:

QHash<QString, bool> isDirectory; //用来存储一个路径是否为目录的信息
QString currentPath; //用来存储现在的路径
QFile *file;

(3)添加槽:

private slots:
void on_downloadButton_clicked();
void on_cdToParentButton_clicked();
void on_connectButton_clicked();
void ftpCommandFinished(int,bool);
void ftpCommandStarted(int);
void updateDataTransferProgress(qint64,qint64 );//更新进度条
//将服务器上的文件添加到Tree Widget中
void addToList(const QUrlInfo &urlInfo);
void processItem(QTreeWidgetItem*,int);//双击一个目录时显示其内容

2.更改widget.cpp的内容。

(1)实现“连接”按钮的单击事件槽。

void Widget::on_connectButton_clicked()   //连接按钮
{
    ui->fileList->clear();
    currentPath.clear();
    isDirectory.clear();

    ftp = new QFtp(this);
    connect(ftp,SIGNAL(commandStarted(int)),
this,SLOT(ftpCommandStarted(int)));
    connect(ftp,SIGNAL(commandFinished(int,bool)),
           this,SLOT(ftpCommandFinished(int,bool)));
    connect(ftp,SIGNAL(listInfo(QUrlInfo)),
this,SLOT(addToList(QUrlInfo)));
    connect(ftp,SIGNAL(dataTransferProgress(qint64,qint64)),
           this,SLOT(updateDataTransferProgress(qint64,qint64)));

    QString ftpServer = ui->ftpServerLineEdit->text();
    QString userName = ui->userNameLineEdit->text();
    QString passWord = ui->passWordLineEdit->text();
    ftp->connectToHost(ftpServer,21); //连接到服务器,默认端口号是21
    ftp->login(userName,passWord);   //登录
}

我们在“连接”按钮的单击事件槽函数中新建了ftp对象,然后关联了相关的信号和槽。这里的listInfo()信号由ftp->list()函数发射,它将在登录命令完成时调用,下面我们提到。而dataTransferProgress()信号在数据传输时自动发射。最后我们从界面上获得服务器地址,用户名和密码等信息,并以它们为参数执行连接和登录命令。

(2)更改ftpCommandFinished()函数。

我们在相应位置做更改。

首先,在登录命令完成时,我们调用list()函数:

ui->label->setText(tr("登录成功"));
ftp->list();   //发射listInfo()信号,显示文件列表   
然后,在下载命令完成时,我们使下载按钮可用,并关闭打开的文件。
ui->label->setText(tr("已经完成下载"));
ui->downloadButton->setEnabled(true);
file->close();     
delete file;

最后再添加一个if语句,处理list命令完成时的情况:

if (ftp->currentCommand() == QFtp::List){
       if (isDirectory.isEmpty())
       { //如果目录为空,显示“empty”
           ui->fileList->addTopLevelItem(
                  new QTreeWidgetItem(QStringList()<< tr("<empty>")));
           ui->fileList->setEnabled(false);
           ui->label->setText(tr("该目录为空"));
       }
}

我们在list命令完成时,判断文件列表是否为空,如果为空,就让Tree Widget不可用,并显示“empty”条目。

(3)添加文件列表函数的内容如下。

void Widget::addToList(const QUrlInfo &urlInfo)  //添加文件列表
{
    QTreeWidgetItem *item = new QTreeWidgetItem;
    item->setText(0, urlInfo.name());
    item->setText(1, QString::number(urlInfo.size()));
    item->setText(2, urlInfo.owner());
    item->setText(3, urlInfo.group());
    item->setText(4, urlInfo.lastModified().toString("MMM dd yyyy"));

    QPixmap pixmap(urlInfo.isDir() ? "../myFtp2/dir.png" : "../myFtp2/file.png");
    item->setIcon(0, pixmap);

    isDirectory[urlInfo.name()] = urlInfo.isDir();
    //存储该路径是否为目录的信息
    ui->fileList->addTopLevelItem(item);
    if (!ui->fileList->currentItem()) {
       ui->fileList->setCurrentItem(ui->fileList->topLevelItem(0));
       ui->fileList->setEnabled(true);
    }
}

ftp->list()函数执行时会发射listInfo()信号,此时就会执行addToList()函数,在这里我们将文件信息显示在Tree Widget上,并在isDirectory中存储该文件的路径及其是否为目录的信息。为了使文件与目录进行区分,我们使用了不同的图标file.pngdir.png来表示它们,这两个图标放在了工程文件夹中。

(4)将构造函数的内容更改如下。

{
    ui->setupUi(this);
    ui->progressBar->setValue(0);
    //鼠标双击列表中的目录时,我们进入该目录
    connect(ui->fileList,SIGNAL(itemActivated(QTreeWidgetItem*,int)),
           this,SLOT(processItem(QTreeWidgetItem*,int)));
}

这里我们只是让进度条的值为0,然后关联了Tree Widget的一个信号itemActivated()。当鼠标双击一个条目时,发射该信号,我们在槽函数中判断该条目是否为目录,如果是则进入该目录。

(5)processItem()函数的实现如下。

void Widget::processItem(QTreeWidgetItem* item,int)  //打开一个目录
{
    QString name = item->text(0);
    if (isDirectory.value(name)) {  //如果这个文件是个目录,则打开
       ui->fileList->clear();
       isDirectory.clear();
       currentPath += '/';
       currentPath += name;
       ftp->cd(name);
       ftp->list();
       ui->cdToParentButton->setEnabled(true);
    }
}

(6)“返回上一级目录”按钮的单击事件槽函数如下。

void Widget::on_cdToParentButton_clicked()  //返回上级目录按钮
{
    ui->fileList->clear();
    isDirectory.clear();
    currentPath = currentPath.left(currentPath.lastIndexOf('/'));
    if (currentPath.isEmpty()) {
       ui->cdToParentButton->setEnabled(false);
       ftp->cd("/");
    } else {
       ftp->cd(currentPath);
    }
    ftp->list();
}

在返回上一级目录时,我们取当前路径的最后一个/之前的部分,如果此时路径为空了,我们就让“返回上一级目录”按钮不可用。

(7)“下载”按钮单击事件槽函数如下。

void Widget::on_downloadButton_clicked()  //下载按钮
{
    QString fileName = ui->fileList->currentItem()->text(0);
    file = new QFile(fileName);
    if (!file->open(QIODevice::WriteOnly))
    {
       delete file;
       return;
}
//下载按钮不可用,等下载完成后才可用
    ui->downloadButton->setEnabled(false);    ftp->get(ui->fileList->currentItem()->text(0), file);
}

在这里我们获取了当前项目的文件名,然后新建文件,使用get()命令下载服务器上的文件到我们新建的文件中。

(8)更新进度条函数内容如下。

void Widget::updateDataTransferProgress( //进度条
                  qint64 readBytes,qint64 totalBytes)
{
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(readBytes);
}

3.流程说明。

整个程序的流程就和我们实现函数的顺序一样。用户在界面上输入服务器的相关信息,然后我们利用这些信息进行连接并登录服务器,等登录服务器成功时,我们列出服务器上所有的文件。对于一个目录,我们可以进入其中,并返回上一级目录,我们可以下载文件,并显示下载的进度。

对于ftp的操作,全部由那些命令和信号来完成,我们只需要调用相应的命令,并在其发出信号时,进行对应的处理就可以了。而对于文件的显示,则是视图部分的知识了。

4.运行程序,效果如下图所示。

结语

最后需要说明的是,因为为了更好的讲解知识,使得程序简单化,所以我们省去了很多细节上的处理,如果需要,你可以自己添加。比如断开连接和取消下载,你都可以使用

ftp->abort()函数。你也可以参考Qt自带的Ftp Example例子。对于其他操作,比如上传等,你可以根据需要添加。

FTP的相关编程就讲到到这里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当vivo领先后,该学着“快”起来了

文|智能相对论 作者|佘凯文 随着一个个大热球队的爆冷&#xff0c;今年世界杯赛事正在渐入高潮。就目前的形势来看&#xff0c;世界杯最大的悬念在于究竟是“老王”圆梦&#xff0c;还是“新王”登基&#xff0c;又或者是摩洛哥这只黑马一黑到底。 新老球星的更迭&#xff0…

Selenium浏览器自动化测试框架

selenium简介 介绍 Selenium [1] 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。支持的浏览器包括IE&#xff08;7, 8, 9, 10, 11&#xff09;&#xff0c;Mozilla Firefox&#xff0c;Safari&#xff0c;Google C…

设计模式概述(一)

很多小伙伴&#xff0c;不知道设计模式是什么&#xff1f; 通常我们所说的设计模式是一种设计方案&#xff0c;是前人留下的经验及最佳实践。 想要学习设计模式&#xff0c;至少要把面向对象的基本结构全部了解。 设计模式&#xff0c;是建立在一定基础上的思维训练。 学习设…

macos12.6安装配置opencv4.6.0+opencv contrib4.6.0

1.系统准备好&#xff0c;安装依赖python版本高一点的&#xff0c;并且安装上numpy。 brew install cmake gui及cmake&#xff0c;可能装cmake gui的时候有点问题&#xff0c;先装gui再装cmake。 2.提前下载源码&#xff0c;Releases - OpenCV要求opencv与opencv contrib版本一…

Metal每日分享,UV去雾滤镜效果

本案例的目的是理解如何用Metal实现去雾效果滤镜&#xff0c;类似于UV过滤器&#xff1b; Demo HarbethDemo地址 实操代码 // 去雾效果滤镜 let filter C7Haze.init(distance: 0.5, slope: 0.5)// 方案1: ImageView.image try? BoxxIO(element: originImage, filters: [f…

MyBatis ---- MyBatis的逆向工程

MyBatis ---- MyBatis的逆向工程1. 创建逆向工程的步骤a>添加依赖和插件b>创建MyBatis的核心配置文件c>创建逆向工程的配置文件d>执行MBG插件的generate目标2. QBC查询正向工程&#xff1a;先创建 Java 实体类&#xff0c;由框架负责根据实体类生成数据库表。Hiber…

Spring源码深度解析:十五、@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean

一、前言 文章目录&#xff1a;Spring源码深度解析&#xff1a;文章目录 在上篇中我们概述了Aop 实现的逻辑&#xff0c;但是由于篇幅原因&#xff0c;我们将一部分内容拆成了中篇和下篇内容。本篇即中篇&#xff0c;内容主要是讲述 在 Bean创建过程中Aop 挑选适用于当前Bean…

尚医通-MyBatistPlus:修改和自动填充-乐观锁(三)

目录&#xff1a; &#xff08;1&#xff09;MyBatistPlus&#xff1a;修改和自动填充 &#xff08;2&#xff09;MyBatis-Plus-乐观锁 &#xff08;1&#xff09;MyBatistPlus&#xff1a;修改和自动填充 1、更新操作 注意&#xff1a;update时生成的sql自动是动态sql&…

14 - 局部段描述符表

---- 整理自狄泰软件唐佐林老师课程 文章目录1. 什么是局部段描述符表&#xff08;LDT&#xff09;1.1 局部段描述符选择子1.2 局部段描述符表LDT1.3 问题1.4 LDT的定义和使用2. 编程实验&#xff1a;使用LDT实现新功能3. 多任务程序设计的实现思路4. 待解决的问题&#xff1a;…

第51篇 Qt 5.5全新的开始

导语 时间转眼而逝&#xff0c;看一下上次发的教程&#xff0c;已经是一年前的事情了。这一年发生了很多事情&#xff0c;包括自己也包括Qt。当然&#xff0c;自己很忙或者说为了编写《Qt 5编程入门》这些理由&#xff0c;并不能为一年的搁置进行开脱&#xff0c;所以这里首先…

人工智能前沿——6款AI绘画生成工具

>>>深度学习Tricks&#xff0c;第一时间送达<<< 目录 一、【前言】 二、【6款AI绘画生成工具】 1.DeepAI 2.NightCafe 3.Deep Dream Generator 4.StarryAI 5.Fotor 6.Pixso 一、【前言】 AI不仅影响商业和医疗保健等行业&#xff0c;还在创意产业中…

Flutter Web CORS解决方案2-代理转发协议

Flutter Web CORS解决方案2local-cors-proxyshelf_proxydart run shelf_proxyflutter run --dart-definewrap with shellhelp & usagemode & confrun & debuglaunch with proxyvscodeAndroid Studio遗留问题本文介绍第二种解决FlutterWeb CORS问题的方案&#xff1…

[附源码]Nodejs计算机毕业设计基于web的企业人事管理系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

Centos7下Samba服务器配置

环境 vm下centos7.6&#xff0c;IP地址&#xff1a;192.168.139.200 Samba概述 Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件&#xff0c;由服务器及客户端程序构成。SMB&#xff08;Server Messages Block&#xff0c;信息服务块&#xff09;是一种在局域网上共享文…

STM32H747AGI6技术、STM32H747AII6规格、STM32H747BGT6产品概述

产品概述&#xff1a;STM32H7高性能MCU基于高性能Arm Cortex-M7 32位RISC内核&#xff0c;工作频率高达400MHz。Cortex-M7内核具有浮点单元 (FPU) 精度&#xff0c;支持Arm双精度&#xff08;符合IEEE 754标准&#xff09;和单精度数据处理指令与数据类型。STM32H7 MCU支持全套…

第48篇 进阶(八) 3D绘图简介

导语 OpenGL是一个跨平台的用来渲染3D图形的标准API。在Qt中提供了QtOpenGL模块&#xff0c;从而很轻松地实现了在Qt应用程序中使用OpenGL&#xff0c;这主要是在QGLWidget类中完成的。因为3D绘图涉及到了专业方面的内容&#xff0c;我们下面只是讲解最简单的使用&#xff0c;…

Java基础之LinkedList

Java基础之LinkedList一、介绍二、add()一、介绍 底层是一个双向链表实现的List&#xff0c;内部每一个节点采用内部类Node表示&#xff0c;通过first、last引用分别指向链表的第一和最后一个元素非线程安全&#xff0c;可以用Collections.synchronizedList()方法对其进行包装…

Android入门第44天-Android里使用动态BroadCast

BroadCast是什么 BroadcastReceiver就是应用程序间的全局大喇叭&#xff0c;即通信的一个手段&#xff0c; 系统自己在很多时候都会发送广播&#xff0c;比如电量低或者充足&#xff0c;刚启动完&#xff0c;插入耳机&#xff0c;你有一条新的微信消息。。。这种都是使用Broad…

Windows实时运动控制软核(二):LOCAL高速接口测试之Qt

今天&#xff0c;正运动小助手给大家分享一下MotionRT7的安装和使用&#xff0c;以及使用Qt对MotionRT7开发的前期准备。 01 MotionRT7简介 MotionRT7是深圳市正运动技术推出的跨平台运动控制实时内核&#xff0c;也是国内首家完全自主自研&#xff0c; 自主可控的Windows运动…

redis的客户端

关系型数据库存储在磁盘当中&#xff0c;非关系型数据库存储在内存中 Jedis 第一步&#xff1a;导入依赖包 <dependencies><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</vers…