Qt:FTP 与 QFtp 实现文件传输(C++: ftplib)

2023-11-09

目录

C++ 与 FTP

1.1 简介

1.2 工作原理

1.2.1 独特优势

1.2.2 基本模型

1.3 用户与传输

1.3.1 用户分类

1.3.2 传输方式

1.3.3 传输模式

1.4 控制命令

实现方式

2.1 QFtp

2.2 QNetworkAccessManager

2.3 FTPClientSession

2.4 区别

QFtp

3.1 下载编译

3.2 部署依赖

3.3 应用示例

 demo


C++ 与 FTP

C++的简单FTP客户端实现(一)FTP基础知识_c++ ftp-CSDN博客

C++的简单FTP客户端实现(二)编程_c++ftp_xuruilll的博客-CSDN博客

  • Websocket 实现

C/C++实现FTP客户端,实现上传、下载、删除功能_zao_yu的博客-CSDN博客

  • ftplib

mirrors / mkulke / ftplibpp · GitCode

1.1 简介

  • 文件传输协议(File Transfer Protocol),用于网络上进行文件传输的一套标准协议,工作在 OSI 模型的第七层,TCP 模型的第四层,即应用层使用 TCP 传输

  • 允许用户以文件操作的方式(增删改查、传送等)与另一主机相互通信;

  • 用 FTP 程序访问远程资源,实现用户往返传输文件、目录管理以及访问电子邮件等,即使双方配有不同操作系统和文件存储方式。

1.2 工作原理

1.2.1 独特优势

在两台通信的主机之间使用了两条 TCP 连接,一条是数据连接,用于数据传送,是全双工的,允许同时进行双向数据传输;另一条是控制连接,用于传送控制信息(内部命令和命令的响应等控制信息)。

1.2.2 基本模型

客户端有三个构件:用户接口、客户端控制进程、客户端数据传送进程;

服务器有两个构件:服务器控制进程、服务器数据传送进程;

整个交互的 FTP 会话中,控制连接始终是处于连接状态的,数据连接则在文件每一次打开后关闭。

1.3 用户与传输

1.3.1 用户分类

  1. Real 账户:FTP 服务上拥有的账号,默认主目录就是账号命名的目录;

  2. Guest 用户:特定用户所设置的账户,只能访问自己的主目录;

  3. Anonymous 用户:匿名访问,没有指定账户,但可以访问某些公共资源。

1.3.2 传输方式

  • ASCII 传输方式

    当用户正在拷贝的文件包含简单的 ASCII 码文本,且远程服务器上运行的不是 UNIX,则文件传输时通常会自动调整文件内容以便于解析成其他计算机存储文件的格式。

  • 二进制传输方式

    保存文件的位序,以便原始和拷贝的是逐位一一对应的(ASCII 方式一般假设每一字符的第一有效位无意义,而二进制文件传输时所有位都是重要的)。

1.3.3 传输模式

FTP 支持两种模式:Standard(PORT,主动方式)、Passive(PASV,被动方式)。

  • 主动模式

    • 客户端和服务端的 TCP 21 端口建立连接,用来发送命令,客户端需要接收数据的时候在通道上发送 PORT 命令;

    • 传送数据的时候,服务端通过自身的 TCP 20 端口连接客户端指定端口发送数据。

  • 被动模式

    • 建立控制通道和 Standard 模式类似,但建立连接后发送 PASV 命令;

    • 服务端收到 PASV 命令后,打开一个临时端口(1023~65535)并通知客户端在该端口上传送数据。

  • 使用
  1. PORT:服务端到客户端传送数据;PASV:客户端到服务端传送数据;

  2. 客户端在防火墙后或内网,PORT 开放端口,服务端很难透过防火墙进行连接的,PASV 模式等待服务器开放端口后进行连接即可;

两种模式的登录过程,都是客户端去连接服务端。

1.4 控制命令

语法

ftp [-d] [-g] [-i] [-n] [-v] [-f] [-k realm] [-q[-C]][HostName [Port]]
  • 登录:
ftp 用户名@ip:端口号 
# 输入密码
  • 目录:
# 查看目录
ls
dir
# 进入目录
cd 目录
# 进入本地目录
lcd 目录
# 查看本地目录
lls
# 上传文件
put [名称] [目录:所有文件]
mput
# 上传文件夹
put -r [目录:所有文件和文件夹]
# 新建
mkdir
# 修改
rename prename newname
# 删除
rm
# 下载
get [文件名称] [-r 文件夹名称]
mget

实现方式

三种实现 Qt 项目中的 FTP 传输功能:

  1. Qt4 QFtp

  2. Qt5 QNetworkAccessManager

  3. POCO FTPClientSession

2.1 QFtp

    QFtp 是 Qt4 中专门负责 FTP 传输的类,包括创建目录、删除目录、删除文件、获取文件列表、上传、下载等等常规操作,接口如下。

int setProxy(const QString &host, quint16 port);

int connectToHost(const QString &host, quint16 port=21);

int login(const QString &user = QString()
          , const QString &password = QString());

int close();

int setTransferMode(TransferMode mode);

int list(const QString &dir = QString());
int cd(const QString &dir);

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

int put(const QByteArray &data, const QString &file
        , TransferType type = Binary);
int put(QIODevice *dev, const QString &file, TransferType type = Binary);

int remove(const QString &file);

int mkdir(const QString &dir);
int rmdir(const QString &dir);

int rename(const QString &oldname, const QString &newname);

2.2 QNetworkAccessManager

    QNetworkAccessManager 是 Qt 专门负责网络请求的模块,包含了 http 的 post、get、put,put 用来 Qt5 实现 FTP 上传功能,get 用来实现下载功能,仅仅只能上传下载,接口如下。

*put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request
                    , const QByteArray &data);
QNetworkReply *put(const QNetworkRequest &request
                    , QHttpMultiPart *multiPart);

QNetworkReply *get(const QNetworkRequest &request);

2.3 FTPClientSession

    FTPClientSession 是 POCO 中网络模块里负责实现 FTP 的类,接口如下,POCO 是目前最流行的 C++ 轻量级封装库之一,里面包含了很多常用的封装库。

void setTimeout(const Poco::Timespan& timeout);
Poco::Timespan getTimeout() const;

void setPassive(bool flag, bool useRFC1738 = true);
bool getPassive() const;

virtual void open(const std::string& host, Poco::UInt16 port
                , const std::string& username = ""
                , const std::string& password = "");
virtual void login(const std::string& username
                    , const std::string& password);
void logout();
void close();

std::string systemType();

void setFileType(FileType type);
FileType getFileType() const;

void setWorkingDirectory(const std::string& path);
std::string getWorkingDirectory();

void cdup();
void rename(const std::string& oldName, const std::string& newName);
void remove(const std::string& path);
void createDirectory(const std::string& path);
void removeDirectory(const std::string& path);

std::istream& beginDownload(const std::string& path);
void endDownload();
std::ostream& beginUpload(const std::string& path);
void endUpload();

std::istream& beginList(const std::string& path = ""
                        , bool extended = false);
void endList();

void abort();

int sendCommand(const std::string& command, std::string& response);
int sendCommand(const std::string& command
                , const std::string& arg, std::string& response);

bool isOpen() const;
bool isLoggedIn() const;
bool isSecure() const;

const std::string& welcomeMessage();

2.4 区别

  • QFtp:

优点:接口完整;

缺点:源码编译,需要解决编码问题,异常处理不友好(断网,异常信号,连接状态不会改变等);

  • QNetworkAccessManager

优点:简单,集成度高;

缺点:不可文件操作,通过二进制方式上传,且不可改变上传方式;

  • FTPClientSession

优点:使用简单,可异常捕捉;

缺点:缺少上传下载的进度接口或回调,可通过流的方式获取文件列表,但文件名带有空格时读取内容异常。

QFtp

实现 QFtp 的传输,可以通过源码文件进行项目编写,也可以通过编译的方式进行,以下通过编译进行举例。

3.1 下载编译

  1. 下载:qt/qtftp (github.com)

  2. 项目 .pro 文件:”qtftp-master\src\qftp\qftp.pro“

// 源文件
#CONFIG += static
#CONFIG -= shared
// 静态库文件 .a
CONFIG += staticlib 
// 动态库文件 .dll
CONFIG += shared
  1. 头 .h 文件: “qtftp-master\src\qftp\qftp.h”
//#include <QtFtp/qurlinfo.h>
#include <qurlinfo.h>
  1. 进行编译

3.2 部署依赖

  • 动态库文件 .dll,拷贝至 “E:\Qt\V5.9.3\5.9.3\mingw53_32\bin” 下:

  • 静态库文件 .a,拷贝至 “E:\Qt\V5.9.3\5.9.3\mingw53_32\lib” 下:

  • 头文件拷贝至 “E:\Qt\V5.9.3\5.9.3\mingw53_32\include\QtNetwork” 下,同时添加无后缀名文件 QFtp:

QFtp,文本打开,保存:#include "qftp.h"

  • 使用编译库:
// 动态库添加
// 项目 —— 右键 —— 添加库 —— 外部库,动态
win32:CONFIG(release, debug|release): LIBS += 
        -L$$PWD/../../../Qt/V5.9.3/5.9.3/mingw53_32/lib/ -lQt5Ftp
else:win32:CONFIG(debug, debug|release): LIBS +=
        -L$$PWD/../../../Qt/V5.9.3/5.9.3/mingw53_32/lib/ -lQt5Ftpd

INCLUDEPATH += $$PWD/../../../Qt/V5.9.3/5.9.3/mingw53_32/include
DEPENDPATH += $$PWD/../../../Qt/V5.9.3/5.9.3/mingw53_32/include

3.3 应用示例

    参考链接,所有的 QFtp 传输操作,都会涉及到两个信号的发出:

void commandStarted(int);
void commandFinished(int, bool);

    connect(&ftp,&QFtp::commandFinished,this,[&](){
        if(ftp.currentCommand() == QFtp::List){
            qDebug() << "commandFinished. QFtp::List " << fileList ;
            ui->listWidget->addItems(fileList);
            if(m_loop.isRunning())
                m_loop.exit(); // FTP 属于异步操作,事件循环做同步操作
        }
    });
  • 连接与登录
/// 1. 连接服务端
    QString host = "127.0.0.1";
    quint16 port = 21;
    if (ftp->state() != QFtp::LoggedIn)
    {
        ftp->connectToHost(host, port);
        qDebug() << QString("connectToHost : ") << QString::number(
                        ftp->connectToHost(host, port));

        /// 2. 登录账户
        
        ftp->login(username, pwd);
    }
  • 设置模式并传输
/// 3. 设置传输模式: 被动模式
        ftp->setTransferMode(QFtp::Passive);

    /// 4. 操作
    ftp->mkdir(GbkAndUtf8("1as2to3文件夹4_5")); // 新建文件夹,中文处理

    QFile file("F:\\server\\123.txt");
    if(file.open(QIODevice::ReadOnly)){
        ftp->put(&file, QFileInfo(file).fileName()); // 文件上传
    }

    QString filelist = "/";
    ftp->list(filelist); // 刷新指定目录
  • 读取文件列表
void MainWnd::addToList(const QUrlInfo url)
{
    QString
        fileSize; // 用于存储文件大小,根据文件大小字节,设置文件在树列表的单位
    if (urlInfo.size() >= 0 && urlInfo.size() < 1024) {
        fileSize = QString::number(urlInfo.size()) + "Byte";
    }
    else if (urlInfo.size() >= 1024 && urlInfo.size() < 1024 * 1024) {
        fileSize = QString::number(urlInfo.size() / 1024.0, 'f', 2) + "KB";
    }
    else if (urlInfo.size() >= 1024 * 1024 &&
             urlInfo.size() < 1024 * 1024 * 1024) {
        fileSize =
            QString::number(urlInfo.size() / 1024 / 1024.0, 'f', 2) + "MB";
    }
    else if (urlInfo.size() >= 1024 * 1024 * 1024) {
        fileSize =
            QString::number(urlInfo.size() / 1024 / 1024 / 1024.0, 'f', 2) +
            "GB";
    }

    QTreeWidgetItem* item = new QTreeWidgetItem;

    item->setText(0, fromSpecialEncoding(urlInfo.name()));
    item->setText(1, fileSize);
    item->setText(2, urlInfo.lastModified().toString("yyyy/MM/dd hh:mm"));
    item->setText(3, urlInfo.owner());
    item->setText(4, urlInfo.group());


    QPixmap pixmap(urlInfo.isDir() ? ":/img/img/dir.png" :
                                     ":/img/img/file.png");
    item->setIcon(0, pixmap);
    isDirectory[urlInfo.name()] = urlInfo.isDir();
    ui.treeWidget->addTopLevelItem(item);
    if (!ui.treeWidget->currentItem()) {
        ui.treeWidget->setCurrentItem(ui.treeWidget->topLevelItem(0));
        ui.treeWidget->setEnabled(true);
    }
}

目录列表刷新信号:

connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo))         ,SIGNAL(listInfo(QUrlInfo)));

自定义槽函数实现:

connect(ftp, SIGNAL(listInfo(const QUrlInfo &)),this         , SLOT(addToList(const QUrlInfo)));

// QUrlInfo 包含文件、文件夹的各个信息,以此实现刷新读取文件列表

  • 传输进度条
connect(&ftp,&QFtp::dataTransferProgress,this,[=,&fileInfo,&ftp](
        qint64 readBytes, qint64 totalBytes)
{ 
    ui->progressBar->setValue(static_cast<int>(readBytes/totalBytes));

    if(readBytes/totalBytes == 1){
        if(file->isOpen()){
            file->close();
        }
        ftp.disconnect();
        ftp.close();
    }
});
  • 编码格式转换
QString FtpUpload::fromSpecialEncoding(const QString& inputStr)
{
    if (ui.CB_EncodingFormat->currentIndex() == 0) {
        QTextCodec* codec = QTextCodec::codecForName("gbk");
        return codec->toUnicode(inputStr.toLatin1());
    }
    else {
        QTextCodec* codec = QTextCodec::codecForName("utf8");
        return codec->toUnicode(inputStr.toLatin1());
    }
}

QString FtpUpload::toSpecialEncoding(const QString& inputStr)
{
    if (ui.CB_EncodingFormat->currentIndex() == 0) {
        QTextCodec* codec = QTextCodec::codecForName("gbk");
        return QString::fromLatin1(codec->fromUnicode(inputStr));
    }
    else {
        QTextCodec* codec = QTextCodec::codecForName("utf8");
        return QString::fromLatin1(codec->fromUnicode(inputStr));
    }
}
  • 返回上一级
currentPath = currentPath.left(currentPath.lastIndexOf("/"));
ftp.cd(currentPath);

 demo

​​​​​​Qt-Qftp: Qftp 实现 ftp 上传,文件以及文件夹结构,处理定时上传 (gitee.com) 

2782694792/Qt-Ftp (github.com)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt:FTP 与 QFtp 实现文件传输(C++: ftplib) 的相关文章

随机推荐

  • 在Java中使用HttpURLConnection发送http客户端请求、服务器端返回信息、接受服务器端的信息

    在最近的项目中要用到后台发送http请求 post 方式 获取服务器端返回哦信息 下面是自己做的一个简单的演示示例 首先创建一个web工程 简单的一个web工程就好 里面创建两个jsp和一个servlet 其中jsp作为客户端 而servl
  • Ubuntu个人终端常用命令

    Ubuntu终端提供了丰富的命令行工具和功能 现将一些基本的Ubuntu终端命令记录下来 方便查看 1 文件和目录管理命令 ls 列出当前目录下的文件和文件夹 cd 切换到指定目录 pwd 显示当前工作目录的路径 mkdir 创建新目录 r
  • Android Studio安装和使用教程(全文图解)

    目录 JDK安装与配置 一 下载JDK 二 JDK安装 三 JDK的环境配置 四 JDK的配置验证 Android studio安装 Android studio连接手机真机调试 以华为鸿蒙为例 一 新建一个android项目 二 进入项目
  • LeetCode刷题笔记:669.修剪二叉搜索树

    1 问题描述 给你二叉搜索树的根节点 root 同时给定最小边界low 和最大边界 high 通过修剪二叉搜索树 使得所有节点的值在 low high 中 修剪树 不应该 改变保留在树中的元素的相对结构 即 如果没有被移除 原有的父代子代关
  • Chrome浏览器的跨域设置

    做前后端分离的开发的时候 出于一些原因往往需要将浏览器设置成支持跨域的模式 而且chrome浏览器支持可跨域的设置 但是新版本的chrome浏览器提高了跨域设置的门槛 原来的方法不再适用了 其实网上也有很多大神总结的chrome跨域设置教程
  • 去耦电容的选择举例

    在高速时钟电路中 尤其要注意元件的RF去耦问题 究其原因 主要是因为元件会把一部分能量耦合到电源 地系统之中 这些能量以共模或差模RF的形式传播到其他部件中 陶瓷片电容需要比时钟电路要求的自激频率更大的频率 这样可选择一个自激频率在10 3
  • JavaWeb酒店管理系统

    酒店管理系统 一 项目介绍 1 项目用到的技术栈 开发工具 idea 语言 java js html ajax 数据库 MySQL 服务器 Tomcat 框架 mybatis jQuery 2 项目实现功能 管理员和用户登录和退出功能以及用
  • ParameterizedType应用,java反射,获取参数化类型的class实例

    ParameterizedType是一个接口 这个类可以用来检验泛型是否被参数化 比如 class Dao
  • python https RecursionError详解

    RecursionError maximum recursion depth exceeded while calling a Python object File root miniconda3 envs devops lib pytho
  • Ubuntu下安装Pytorch

    安装CPU版本 https www cnblogs com wangqinze p 13407610 html 完成后检验是否有误 python import torch import torchvision 若没有报错则完成 安装GPU版
  • CMake I 设置语言标准

    目录 一
  • docker基础篇-docker的安装、配置、卸载,自定义镜像和搭建公有和私有镜像仓库

    本篇主要是docker的安装 配置 卸载 自定义镜像和搭建公有和私有镜像仓库 以及常规软件安装示例 mysql主从 redis主从搭建 Dockerfile解析及docker compose编排等见另一篇文章 docker高级篇 mysql
  • python之正则表达式:匹配ip地址

    首先分析ip地址的特征 255 255 255 255 1位 0 9 0 9 d 2位 10 99 1 9 d 2位以内 1 9 d 3位 100 199 1 d 2 3位 200 249 2 0 4 d 3位 250 255 25 0 5
  • 如何让opencv成为MATLAB中的一个工具箱

    如何让opencv成为MATLAB中的一个工具箱 前言 谈到图像处理的编程工具 MATLAB和opencv是最经常提到的 两者各有优缺点 比如 MATLAB的数据可视化做得很好 而且还有其它可以方便调用的工具箱 但是在图像的处理上面 不如o
  • IDEA快速生成方法

    快捷键 Alt Insert 按下快捷键之后会弹出如下界面 依次是Constructor方法 Getter方法 Setter方法 Getter 和Setter方法 equa 和 hashCode 方法 toString方法 Override
  • 三大通信协议(3)SPI——寄存器配置

    目录 一 SPI通信协议简介 二 SPI通信时序 1 主从通信 2 模式选择 三 实例 总结 一 SPI通信协议简介 SPI是串行外设接口 Serial Peripheral Interface 的缩写 是 Motorola 公司推出的一种
  • windows下expect使用小结

    最近因为学习tcl脚本语言 需要用到expect扩展包 结合网上的资料和自己的学习情况做一下整理 expect是一种基于TCL的解释型脚本语言 能够实现自动和交互式任务进行通信 而无需人的干预 expect由一系列expect send对组
  • SpringBoot结合MyBatis实现多数据源配置

    SpringBoot结合MyBatis实现多数据源配置 一 前提条件 1 1 环境准备 SpringBoot框架实现多数据源操作 首先需要搭建Mybatis的运行环境 由于是多数据源 也就是要有多个数据库 所以 我们创建两个测试数据库 分别
  • Unity【LayerMask】层级关系以及代码对层级的控制

    LayerMask的存储 LayerMask的每一个层级都是一个二进制数字 实际上LayerMash是32位的二进制记录的 每一个层级对应一个二进制位置 所以理论上Unity最多能有32个层级关系 为了方便解释以下列表假设只有5位二进制数据
  • Qt:FTP 与 QFtp 实现文件传输(C++: ftplib)

    目录 C 与 FTP 1 1 简介 1 2 工作原理 1 2 1 独特优势 1 2 2 基本模型 1 3 用户与传输 1 3 1 用户分类 1 3 2 传输方式 1 3 3 传输模式 1 4 控制命令 实现方式 2 1 QFtp 2 2 Q