Qt+QSSH实现SFTP

1.下载qssh源码

github链接
https://github.com/mardy/QSsh/tree/botan-1

Gitee链接
https://gitee.com/shikai1995/qssh-botan-1/tree/botan-1/

需要使用botan分支

2.编译

使用QtC打开qssh.pro,编译src项目

3.获取相关库

lib文件夹里有lib和dll,分别时botan和qssh的;src/libs/ssh里有头文件(可以把h文件以外的都删除);

拿出来分别放在lib文件夹和include文件夹里,dll放在可执行程序路径

4.使用,pro

# qssh库使用
win32 {
CONFIG(debug, debug|release) {
    LIBS += -L$$PWD/qssh/lib/ -lQSshd
    LIBS += -L$$PWD/qssh/lib/ -lBotand
} else {
    LIBS += -L$$PWD/qssh/lib/ -lQSsh
    LIBS += -L$$PWD/qssh/lib/ -lBotan
}
}
INCLUDEPATH += $$PWD/qssh/include

5.头文件封装

#ifndef FTPCLIENT_H
#define FTPCLIENT_H

#include <QObject>
#include "BaseComm.h"
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QFile>
#include <QFileInfo>
#include <QEvent>

#include "sshconnection.h"
#include "sftpchannel.h"


class FtpClient : public BaseComm
{
    Q_OBJECT
public:
    explicit FtpClient(CommPlugin* commPlugin, QObject* parent = nullptr);
    ~FtpClient();
    struct TransforJob {
        QString localFilePath = "";
        QString remoteFilePath = "";
        bool isUploadFile = true;
        SFtpServerConfig sFtpServerConfig;
    };

    void appendTrasforFileJob(const QString& localFilePath,          //创建传输文件的任务
                             const QString& remoteFilePath,
                             const SFtpServerConfig& cfg,
                              bool isUploadFile);

signals:
    void transforFileSig();

private slots:
    void on_init();
    void on_onnected();                                                 //连接服务器成功
    void on_transforFileSig();

private:

    void loopTrasforFile();         //线程函数,循环获取文件传输任务列表里的任务并执行
    void trasforFile(const TransforJob& job);
private:

    QSharedPointer<QSsh::SftpChannel>       m_sftpChannel   = nullptr;  //sftp传输通道
    std::shared_ptr<QSsh::SshConnection>    m_sshConnection = nullptr;  //sftp连接对象


    std::queue<TransforJob> m_transforJobs;
    TransforJob             m_curTransforJob;//当前正在传输的job
    std::mutex              m_transforJobMutex;
    std::condition_variable m_transforJobCdt;

    std::atomic_bool        m_isTransforing = { false };    //正在传输文件标志位
    std::atomic_bool        m_working       = { true };     //获取文件传输任务线程运行标志位
};

#endif // FTPCLIENT_H

6.cpp文件封装

#include "FtpClient.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QTimer>
#include <QThread>


FtpClient::FtpClient(CommPlugin* commPlugin, QObject* parent)
    : BaseComm{ commPlugin, parent }
{

}

FtpClient::~FtpClient()
{
    m_working = false;
}

void FtpClient::on_init()
{
    //[1] 创建线程循环从任务列表获取任务并执行
    std::thread threadTrasforFile(&FtpClient::loopTrasforFile, this);
    threadTrasforFile.detach();

    //[2] 订阅消息


    QObject::connect(this, &FtpClient::transforFileSig, this, &FtpClient::on_transforFileSig);
}


void FtpClient::appendTrasforFileJob(const QString &localFilePath, const QString &remoteFilePath, const SFtpServerConfig &cfg, bool isUploadFile)
{
    //[1] 创建传输任务
    TransforJob transforJob;
    transforJob.isUploadFile = isUploadFile;
    transforJob.localFilePath = localFilePath;
    transforJob.remoteFilePath = remoteFilePath;
    transforJob.sFtpServerConfig = cfg;

    //[2] 将任务放到任务队列中
    std::unique_lock<std::mutex> locker(m_transforJobMutex);
    m_transforJobs.push(transforJob);
    locker.unlock();

    //[3] 如果当前没有传输任务则告诉传输线程可以传输
    if(!m_isTransforing)
    {
        m_transforJobCdt.notify_one();
    }
}

void FtpClient::loopTrasforFile()
{
    while(m_working)    //只要程序没有关就一直从任务池里获取任务并执行
    {
        std::unique_lock<std::mutex> locker(m_transforJobMutex);

        //[1] 判断是否有任务
        while(m_transforJobs.empty())
        {
            m_transforJobCdt.wait(locker);
        }
        //[2] 如果有正在执行的任务则退回
        if(m_isTransforing)
        {
            QThread::msleep(100);
            continue;
        }
        //[3] 获取队列中的首个任务
        m_curTransforJob = m_transforJobs.front();
        m_transforJobs.pop();
        locker.unlock();

        //[4] 将标志位置为true并开始执行任务
        m_isTransforing = true;
        emit transforFileSig();
    }
}

void FtpClient::on_transforFileSig()
{
    //[1] 创建连接
    QSsh::SshConnectionParameters argParameters;
    argParameters.setHost(QString::fromStdString(m_curTransforJob.sFtpServerConfig.host));
    argParameters.setPort(m_curTransforJob.sFtpServerConfig.port);
    argParameters.setUserName(QString::fromStdString(m_curTransforJob.sFtpServerConfig.userName));
    argParameters.setPassword(QString::fromStdString(m_curTransforJob.sFtpServerConfig.passWord));
    argParameters.timeout = 10;
    argParameters.authenticationType =
            QSsh::SshConnectionParameters::AuthenticationTypePassword; //密码方式连接

    //[2] 状态判断
    if (m_sshConnection)
    {
        m_sshConnection.get()->disconnect();
        m_sshConnection.reset();
        m_sshConnection = nullptr;
    }

    //[3] 连接初始化
    m_sshConnection = std::make_shared<QSsh::SshConnection>(argParameters);

    //[4] 连接成功执行的槽函数
    QObject::connect(m_sshConnection.get(), &QSsh::SshConnection::connected, this, &FtpClient::on_onnected, Qt::DirectConnection);

    //[4] 连接失败执行的槽函数
    QObject::connect(m_sshConnection.get(), &QSsh::SshConnection::error, this, [this](QSsh::SshError err){
        LOG_ERR(QString("sftp连接异常:%1").arg(err).toStdString());
     //dosomething
        m_isTransforing = false;
    },  Qt::DirectConnection);

    //[5] 开始连接服务器
    m_sshConnection->connectToHost();
}

void FtpClient::on_onnected()
{
    do {
        //[1] 连接服务器成功,发送界面提示
        LOG_INFO(QString("连接sftp服务器成功").toStdString());
        {
            //dosomething
        }

        //[2] 创建ftp通道
        if(m_sftpChannel)
        {
            m_sftpChannel.data()->disconnect();
            m_sftpChannel.clear();
            m_sftpChannel = nullptr;
        }
        m_sftpChannel = m_sshConnection->createSftpChannel();
        if (!m_sftpChannel)
        {
            LOG_ERR("sftpChannel create failed.");
            m_isTransforing = false;
            return;
        }

        //[3] chanel初始化成功执行的槽函数
        QObject::connect(m_sftpChannel.data(), &QSsh::SftpChannel::initialized, this, [this]() {
            LOG_INFO(QString("sftp chanel初始化成功").toStdString());
            //dosomethingif(m_curTransforJob.isUploadFile)
            {
                m_sftpChannel->uploadFile(m_curTransforJob.localFilePath,
                                          m_curTransforJob.remoteFilePath,
                                          QSsh::SftpOverwriteExisting);
            }
            else
            {
                m_sftpChannel->downloadFile(m_curTransforJob.remoteFilePath,
                                            m_curTransforJob.localFilePath,
                                            QSsh::SftpOverwriteExisting);
            }
        },  Qt::DirectConnection);

        //[4] chanel有异常执行的槽函数
        QObject::connect(m_sftpChannel.data(), &QSsh::SftpChannel::channelError, this, [this](const QString& reason) {
            LOG_ERR(QString("sftp chanel error:%1").arg(reason).toStdString());
            //dosomething
            m_isTransforing = false;
        },  Qt::DirectConnection);

        //[5] 文件传输完成执行的槽函数
        QObject::connect(m_sftpChannel.data(), &QSsh::SftpChannel::finished, this, [this](QSsh::SftpJobId id, QString error) {
            QString transforFilePath;
            int state = 1;
            if (m_curTransforJob.isUploadFile)
            {
                transforFilePath = m_curTransforJob.localFilePath;
                state = 1;
            }
            else
            {
                transforFilePath = m_curTransforJob.remoteFilePath;
                state = 2;
            }
            //dosomething

            LOG_INFO(QString("文件传输完成:%1").arg(transforFilePath).toStdString());

            //传输完成回收当次连接和通道
            m_sftpChannel->closeChannel();
            m_sftpChannel.reset();
            m_sftpChannel = nullptr;
            m_sshConnection.get()->disconnect();
            m_sshConnection.reset();
            m_sshConnection = nullptr;

            //重置标志位以进行下次传输
            m_isTransforing = false;
        },  Qt::DirectConnection);

        //[6] 初始化channel
        m_sftpChannel->initialize();
    } while (0);

}

 

posted @ 2025-03-28 10:48  朱小勇  阅读(663)  评论(0)    收藏  举报