Qt quick+QSsh第三方库实现SSH连接并下载文件

前言:

1. QSsh mingw环境下编译

2. 配置项目.pro文件,导入LIBS,INCLUDES

3. 添加头文件,调用第三方库函数

第一步实现前端界面QML

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
//import QtGraphicalEffects 1.15 // 导入图形效果模块
import QtQuick.Dialogs 1.2

Window {
    visible: true
    //    width: 640
    //    height: 480
    width: 540
    height: 540
    title: qsTr("高炮软件更新工具v1.0.0")
    color: "#fff"

    minimumWidth: 540 // 设置窗口的最小宽度为300
    minimumHeight: 540 // 设置窗口的最小高度为200

    property color backgroundColor: "#fff"
    property real columnLength: 300
    property real inputLength: 300
    Column {
        anchors.centerIn: parent
        spacing: 15

        Rectangle {
            width: columnLength
            height: 50
            color: backgroundColor
            Text {
                id: welcomeText
                text: "高炮软件更新工具v1.0.0"
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: 20
                color: "#404142"
            }
        }
        /**

          1. ipPath
          2. username
          3. password
          4. remotePath
          5. savepath
          */

        Rectangle {
            width: columnLength
            height: 50
            color: backgroundColor
            TextField {
                id: ipPathInput
                anchors.centerIn: parent
                placeholderText: "输入服务器地址"
                width: inputLength
                // 你可以在这里添加验证器,比如正则表达式验证器
                //                validator: RegExpValidator { regExp: /^(https?|ssh):\/\/[^\s/$.?#].[^\s]*$/ }
            }
        }
        Rectangle {
            width: columnLength
            height: 50
            color: backgroundColor
            TextField {
                id: usernameInput
                anchors.centerIn: parent
                placeholderText: "输入用户名"
                width: inputLength
            }
        }
        Rectangle {
            width: columnLength
            height: 50
            color: backgroundColor
            TextField {
                id: passwordInput
                anchors.centerIn: parent
                placeholderText: "输入密码"
                width: inputLength
                echoMode: TextInput.Password
            }
        }
        Rectangle {
            width: columnLength
            height: 50
            color: backgroundColor
            TextField {
                id: remotePathInput
                anchors.centerIn: parent
                placeholderText: "输入远程文件路径"
                width: inputLength
            }
        }
        Rectangle {
            width: columnLength
            height: 50
            color: "#fff"

            Row{

                spacing: 5

                Rectangle {
                    width: 100
                    height: 50

                    FileDialog {
                        id: folderDialog
                        title: "请选择文件夹"
                        selectFolder: true // 设置为选择文件夹
                        //                        folder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) // 设置默认打开的文件夹位置
                        onAccepted: {
                            console.log("选择的文件夹路径为:", folderDialog.fileUrl)
                        }
                        onRejected: {
                            console.log("文件夹选择被取消")
                        }
                    }

                    Button {
                        text: "选择保存地址"
                        onClicked: {
                            folderDialog.open() // 点击按钮时打开文件夹选择对话框
                        }
                    }

                }
                Rectangle {
                    width: 300
                    height: 50

                    // 用于显示选择的路径
                    Text {
                        id: savepathDisplay
                        width: parent.width * 0.8
                        text: folderDialog.fileUrl
                        anchors.horizontalCenter: parent.horizontalCenter
                        anchors.verticalCenter: parent.verticalCenter
                    }

                }


            }

        }

        Row {
            spacing: 15

            Rectangle {
                width: columnLength*0.48
                height: 50
                id: submit
                color: backgroundColor

                Rectangle {
                    width: 150
                    height: 40
                    id: button
                    radius: 10 // 设置圆角大小
                    color: "#fff" // 设置按钮颜色
                    anchors.centerIn: parent
                    border.width: 1
                    border.color: "black"

                    // 按钮文本
                    Text {
                        id: buttonText
                        text: "连接服务器"
                        anchors.centerIn: parent
                        color: "#404142"
                        font.pixelSize: 16
                    }

                    // 鼠标悬停效果
                    /**

                  1. ipPathInput
                  2. usernameInput
                  3. passwordInput
                  4. remotePathInput
                  5. folderDialog.fileUrl
                  */
                    MouseArea {
                        anchors.fill: parent
                        acceptedButtons: Qt.LeftButton
                        onClicked: {
                            // 按钮点击逻辑
                            console.log("按钮1被点击")
                            var ipPath = ipPathInput.text;
                            var username = usernameInput.text;
                            var password = passwordInput.text;
                            console.log(ipPath+" " +username+" "+password+" ");
                            updateSystem.sigInit(ipPath, username, password, 22)

                        }
                        hoverEnabled: true
                        onEntered: {
                            button.scale = 1.03 // 鼠标悬停时放大
                        }
                        onExited: {
                            button.scale = 1.0 // 鼠标离开时恢复原大小
                        }
                    }

                }
            }


            Rectangle {
                width: columnLength*0.48
                height: 50
                //id: submit
                color: backgroundColor

                Rectangle {
                    width: 150
                    height: 40
                    id: button2
                    radius: 10 // 设置圆角大小
                    color: "#fff" // 设置按钮颜色
                    anchors.centerIn: parent
                    border.width: 1
                    border.color: "black"

                    // 按钮文本
                    Text {
                        //                    id: buttonText
                        text: "下载"
                        anchors.centerIn: parent
                        color: "#404142"
                        font.pixelSize: 16
                    }
                    MouseArea {
                        anchors.fill: parent
                        acceptedButtons: Qt.LeftButton
                        onClicked: {
                            // 按钮点击逻辑
                            console.log("按钮2被点击" + folderDialog.fileUrl)
                            var remotePath = remotePathInput.text;
                            var savepath = folderDialog.fileUrl;
                            updateSystem.sigDownload(savepath, remotePath);
                        }
                        hoverEnabled: true
                        onEntered: {
                            button2.scale = 1.03 // 鼠标悬停时放大
                        }
                        onExited: {
                            button2.scale = 1.0 // 鼠标离开时恢复原大小
                        }
                    }
                }
            }
        }
    }


    Text {
        id: errorText
        text: updateSystem.resultMessage ? updateSystem.resultMessage : ""
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.margins: 10
        anchors.bottomMargin: 20
        font.pixelSize: 16
    }


    // 进度条用于显示下载进度
    ProgressBar {
        id: progressBar
        width: parent.width * 0.8
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.bottom: parent.bottom
        visible: true // 默认不可见,直到开始下载
        value: 0 // 初始进度为0
    }




}

第二步实现SSH连接

第三步实现SFTP通道传输

具体代码

头文件

#ifndef UPDATESYSTEM_H
#define UPDATESYSTEM_H

#include <sshconnection.h>
#include <sshremoteprocess.h>
#include <sftpchannel.h>
#include <sftpfilesystemmodel.h>
#include <QThread>
#include <QTextCodec>
#include <QProgressBar>
#include <QDir>
#include <QCoreApplication>
#include <QSharedPointer>
class UpdateSystem : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString resultMessage READ resultMessage NOTIFY resultMessageChanged)
public:
    explicit UpdateSystem(QString ip = "192.168.6.138", int port = 22, QString user = "qnkj",  QString pwd = "qnkj123");
    ~UpdateSystem();
    QString resultMessage() const;
    QMap<QString, QFile*> downFile();

private:
    // 新建线程,用于 SSH
    QThread * thread = nullptr;
    // SSH 是否已连接
    bool bConnected = false;
    // SSH 是否可发送
    bool bSendable = false;
    // IP 地址
    QString strIp;
    // 端口号
    int nPort;
    // 用户名
    QString strUser;
    // 密码
    QString strPwd;
    // SSH 连接参数
    QSsh::SshConnectionParameters argParameters;
    // SSH Socket
    QSsh::SshConnection * sshSocket = nullptr;
    // 通道进程
//    QSharedPointer<QSsh::SshRemoteProcess> process;
    // 通道指针
    QSsh::SftpChannel::Ptr channel;
    // 远程地址
    QString m_remoteFolderPath = "/home/qnkj/Desktop/a/b/aaa.txt";
    // 本地地址
    QString m_localFolderPath = "C:\\Users\\12008\\Desktop\\";
    // 结果信息
    QString m_message;
    // 进度条
    QProgressBar *progressBar;

//    void downloadDirectory(const QString &remoteDir, const QString &localDir);
//    void downloadDirectory(QSsh::SftpChannel *sftpChannel, const QString &remotePath, const QString &localPath);

private:

    QString getIpPort() {return strIp + ":" + QString::number(nPort);}

signals:
    void sigInit(QString ip,  QString user, QString pwd, int port);
    void sigConnectStateChanged(bool state, QString ip, int port);
    void sigInitParams();
    void sigDownload(const QString &savePath, const QString &remotePath);
    void channelError(QString);
    void finished(QSsh::SftpJobId, QString);
    void resultMessageChanged();

public slots:
    /**
     * @brief createConnection  创建 SSH 连接
     */
    void createConnection();
    void initParams();
    void init(QString ip, QString user, QString pwd, int port = 22);
    void sshConnected();
    void sshConnectError(QSsh::SshError sshError);
    /**
     * @brief createConnection  创建 通道
     */
    void download(const QString &savePath, const QString &remotePath);
    void onChannelInitialized();
    void onChannelError(const QString &error);
    void onDownLoadfinished(QSsh::SftpJobId job, const QString &error);
    void onDownloadProgress(qint64 received, qint64 total);
//    void onReadyReadStandardError();

};

#endif // UPDATESYSTEM_H

实现文件

#include "Updatesystem.h"
#include <QDebug>

UpdateSystem::UpdateSystem(QString ip, int port, QString user, QString pwd)
{
    strIp = ip;
    nPort = port;
    strUser = user;
    strPwd = pwd;
    qDebug() << strIp <<endl;
    qDebug() << nPort <<endl;
    qDebug() << strUser <<endl;
    qDebug() << strPwd <<endl;

    connect(this, &UpdateSystem::sigInit, this, &UpdateSystem::init);
    connect(this, &UpdateSystem::sigInitParams, this, &UpdateSystem::initParams);
    connect(this, &UpdateSystem::sigDownload, this, &UpdateSystem::download);
}

UpdateSystem::~UpdateSystem()
{
    // 断开 SFTP 通道和 SSH 连接
    channel->disconnect();
    sshSocket->disconnectFromHost();
}

QString UpdateSystem::resultMessage() const
{
    return m_message;
}

void UpdateSystem::init(QString ip,  QString user, QString pwd, int port)
{

    if(ip!="") strIp = ip;
    if(pwd!="") nPort = port;
    if(user!="") strUser = user;
    if(pwd!="") strPwd = pwd;

    emit sigInitParams();
}

void UpdateSystem::initParams()
{
    qDebug() << "************************************************************";
    qDebug() << strIp <<endl;
    qDebug() << nPort <<endl;
    qDebug() << strUser <<endl;
    qDebug() << strPwd <<endl;
    qDebug() << "************************************************************";
    argParameters.setHost(strIp);
    argParameters.setPort(nPort);
    argParameters.setUserName(strUser);
    argParameters.setPassword(strPwd);
    argParameters.timeout = 10;

    // 密码方式连接
    argParameters.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePassword;
    // 连接
    createConnection();
}

// 创建 SSH 连接
void UpdateSystem::createConnection()
{
    if(bConnected)
        return;

    if(!sshSocket)
    {
        sshSocket = new QSsh::SshConnection(argParameters);
        connect(sshSocket, SIGNAL(connected()), SLOT(sshConnected()));
        connect(sshSocket, SIGNAL(error(QSsh::SshError)), SLOT(sshConnectError(QSsh::SshError)));
    }

    sshSocket->connectToHost();
    m_message = "正在连接服务器...";
    emit resultMessageChanged(); // 发射信号
}

void UpdateSystem::sshConnected()
{
    qDebug() << "************************************************************";
    qDebug() << "********************SSH  onConnected********************";
    qDebug() << "************************************************************";
    m_message = "服务器连接成功!";
    emit resultMessageChanged(); // 发射信号
}

void UpdateSystem::sshConnectError(QSsh::SshError sshError)
{
    qDebug() << "************************************************************";
    qDebug() << "********************SSH  failed********************";
    qDebug() << "************************************************************";
    qDebug() << sshError<<endl;
    m_message = "连接服务器失败❌";
    emit resultMessageChanged(); // 发射信号
}

void UpdateSystem::download(const QString &savePath ,const QString &remotePath)
{
    qDebug() << "************************************************************";
    qDebug() << savePath <<endl;
    qDebug() << remotePath <<endl;
    qDebug() << "************************************************************";
    if(remotePath!="") m_remoteFolderPath = remotePath;
    if(savePath!="") {
        //        m_localFolderPath = savePath;
        // 检查是否以 "file:///" 开头
        if (savePath.startsWith("file:///")) {
            // 移除 "file:///" 协议头
            m_localFolderPath = savePath.mid(8);
            // 替换所有的正斜杠为反斜杠
            m_localFolderPath.replace("/", "\\");

            // 获取远程文件名
            QRegExp rx("\\/([^\\/]+)$");
            rx.indexIn(m_remoteFolderPath);
            QString remoteFileName = rx.cap(1);
            m_localFolderPath += "\\"+remoteFileName;
            qDebug() << remoteFileName <<endl;

        } else {
            qDebug() << "格式错误!";
        }
    }

    qDebug() << "************************************************************";
    qDebug() << m_localFolderPath <<endl;
    qDebug() << m_remoteFolderPath <<endl;
    qDebug() << "************************************************************";

    channel  = sshSocket->createSftpChannel();
    if (channel) {
        // 连接信号和槽以处理通道初始化失败的情况
        connect(channel.data(), SIGNAL(initialized()),
                SLOT(onChannelInitialized()));

        connect(channel.data(), SIGNAL(channelError(QString)),
                SLOT(onChannelError(QString)));

        connect(channel.data(), SIGNAL(finished(QSsh::SftpJobId, QString)),
                SLOT(onDownLoadfinished(QSsh::SftpJobId, QString)));

        // 连接进度信号
//        connect(channel.data(), &QSsh::SftpChannel::finished,
//                SLOT(onDownloadProgress(qint64 received, qint64 total)));

        // 初始化 SFTP 通道
        channel->initialize();
    } else {
        qDebug() << "——————————SecureDownloader: Error null channel——————————";
    }
}

void UpdateSystem::onChannelInitialized()
{
    qDebug() << "********************SecureUploader: Channel Initialized********************";
    //    downloadDirectory(m_remoteFolderPath, m_localFolderPath);
    QSsh::SftpJobId jobId = channel->downloadFile(m_remoteFolderPath, m_localFolderPath, QSsh::SftpOverwriteExisting);
    if (jobId != QSsh::SftpInvalidJob) {
        qDebug() << "********************SecureUploader: Starting job #********************" << jobId;
    } else {
        qDebug() << "********************SecureUploader: Invalid Job********************";
    }

}

void UpdateSystem::onChannelError(const QString &error)
{
    qDebug() << "********************SFTP channel error:********************" << error;
}

void UpdateSystem::onDownLoadfinished(QSsh::SftpJobId job, const QString &error)
{

    m_message = error.isEmpty() ? "服务器资源下载成功!" : error;
    emit resultMessageChanged(); // 发射信号
    qDebug() << "********************SecureDownloader: Finished download job #" << job << ":" << (error.isEmpty() ? "OK" : error);
}

void UpdateSystem::onDownloadProgress(qint64 received, qint64 total)
{
    qDebug() << "****************************************";
    qDebug() << "****************************************";
    qDebug() << "✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨";
    qDebug() << "✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨";
    if (total > 0) {
        double percentage = static_cast<double>(received) / total * 100.0;
        progressBar->setValue(static_cast<int>(percentage));
    } else {
        progressBar->setValue(0); // 如果总大小未知,则将进度条设置为0
    }
    QCoreApplication::processEvents(); // 强制处理事件队列中的事件
}
             

实现结果

我是在虚拟机里弄了个Ubuntu,用它测试的,ifconfig获取到它的IP地址,默认端口是22,输入用户名和密码,点击连接服务器,连接后输入Ubuntu里你要下载的资源文件的地址,再选择本地的保存地址,点击下载即可。

04-03
关于QSsh的问题,以下是整理的相关信息: --- QSsh 是一个与 SSH 协议相关的概念或工具。SSH(Secure Shell)是一种网络安全协议,主要用于在网络上传输加密数据以及远程登录计算机系统。以下是对 QSsh 的解释及相关内容整合: ### 方法一:理解 QSsh 的定义 QSsh 不是一个官方标准术语,但在某些技术社区中可能指代一种基于 Qt 框架实现SSH 客户端库或工具。它通常用于开发跨平台的应用程序以支持安全连接。 - 如果您正在寻找如何在项目中集成 QSsh 功能,则需要了解其依赖项及安装方式。 - 常见场景包括嵌入式设备管理、自动化脚本执行等。 --- ### 方法二:QSsh 的应用场景 1. **Qt 开发环境中的应用** 对于使用 Qt 进行 GUI 应用开发的技术人员来说,QSsh 提供了一种便捷的方式来处理 SSH 数据流。例如,在图形界面下建立服务器连接或者传输文件。 2. **替代传统命令行工具** 虽然 OpenSSH 已经非常成熟稳定,但当开发者希望构建自己的定制化解决方案时,可以考虑采用类似 QSsh 的第三方组件来完成特定需求。 3. **多操作系统兼容性测试** 利用 QSsh 编写跨 Windows/Linux/macOS 等不同系统的统一接口代码片段如下所示: ```cpp #include <QCoreApplication> #include "qsstools.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 初始化 QSsh 实例对象 SshConnectionParameters params; params.setHost("example.com"); params.setUser("root"); if (connectToSshServer(params)) { qDebug() << "Connected successfully!"; } else { qDebug() << "Failed to connect."; } return a.exec(); } ``` --- ### 方法三:常见问题排查指南 如果您遇到有关 QSsh 的错误提示,请参考以下建议解决办法: 1. **检查是否正确配置了密钥认证机制** 当尝试无密码访问目标主机失败时,请确保公私钥已妥善交换且权限设置适当。具体操作步骤可查阅相关文档说明链接[此处](https://doc.qt.io/qt-solutions/qtsolution-ssh.html)。 2. **验证网络连通状况良好与否** 使用 `ping` 或者其他诊断手段确认源地址能够顺利抵达目的节点之前不存在防火墙阻挡等情况发生。 3. **更新至最新版本避免漏洞风险** 定期关注上游维护者的发布动态及时升级软件包从而减少潜在安全隐患影响业务正常运行。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值