前言:
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里你要下载的资源文件的地址,再选择本地的保存地址,点击下载即可。