文章目录
- 1.重点说明
- 2.测试环境搭建
- 3.代码具体实现
- 步骤1:安装libssh2
- 步骤2:创建C++类处理SFTP文件传输
- 步骤3:将C++类与QML绑定
- 步骤4:创建QML界面
- 步骤5:配置项目文件
- 总结
1.重点说明
FTP、FTPS、SFTP不要混淆,我们这里是SFTP
1.1SFTP
SFTP(SSH File Transfer Protocol,也称 Secret File Transfer Protocol)是一种安全的文件传输协议,一种通过网络传输文件的安全方法;它确保使用私有和安全的数据流来安全地传输数据。
1.2 FTPS
FTPS是一种对常用的文件传输协议(FTP)添加传输层安全(TLS)和安全套接层(SSL)加密协议支持的扩展协议。FTPS不应与基于SSH的SSH文件传输协议或是Secure FTP协议相混淆。
1.3 FTP
FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。在开发网站的时候,通常利用FTP协议把网页或程序传到Web服务器上。此外,由于FTP传输效率非常高,在网络上传输大的文件时,一般也采用该协议。
默认情况下FTP协议使用TCP端口中的 20和21这两个端口,其中20用于传输数据,21用于传输控制信息。但是,是否使用20作为传输数据的端口与FTP使用的传输模式有关,如果采用主动模式,那么数据传输端口就是20;如果采用被动模式,则具体最终使用哪个端口要服务器端和客户端协商决定。
2.测试环境搭建
2.1 Xlight FTP工具
Xlight FTP服务器是非常容易使用的FTP服务器。 为了保证高性能,FTP服务器全部用C语言写成,服务器运行时占用很少的系统资源。它具有高效网络算法,在大量用户情况下, 可以充分利用FTP服务器带宽, 能够轻松处理数千用户的同时下载。
我们使用了Xlight FTP工具,具体安装和配置这里不提供!
3.代码具体实现
在Qt中使用C++调用libssh2实现SFTP文件传输,并使用QML实现一个简单的文件传输页面,需要几个关键步骤:
- 设置并配置libssh2库。
- 创建一个C++类处理SFTP文件传输。
- 将C++类与QML绑定。
- 创建QML界面来进行文件传输。
步骤1:安装libssh2
确保你已经安装了libssh2库。如果没有安装,可以使用以下命令进行安装:
-
Ubuntu/Debian:
sudo apt-get install libssh2-1-dev
-
macOS:
brew install libssh2
步骤2:创建C++类处理SFTP文件传输
创建一个C++类,用于处理与SFTP服务器的连接和文件传输。
sftpclient.h
#ifndef SFTPCLIENT_H
#define SFTPCLIENT_H
#include <QObject>
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <QFile>
class SFTPClient : public QObject
{
Q_OBJECT
public:
explicit SFTPClient(QObject *parent = nullptr);
~SFTPClient();
Q_INVOKABLE bool connectToServer(const QString &hostname, const QString &username, const QString &password, int port = 22);
Q_INVOKABLE bool uploadFile(const QString &localFilePath, const QString &remoteFilePath);
signals:
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
void uploadFinished(bool success);
private:
LIBSSH2_SESSION *session;
LIBSSH2_SFTP *sftpSession;
int sock;
bool initLibssh2();
void cleanupLibssh2();
};
#endif // SFTPCLIENT_H
sftpclient.cpp
#include "sftpclient.h"
#include <QDebug>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
SFTPClient::SFTPClient(QObject *parent) : QObject(parent), session(nullptr), sftpSession(nullptr), sock(-1)
{
initLibssh2();
}
SFTPClient::~SFTPClient()
{
cleanupLibssh2();
}
bool SFTPClient::initLibssh2()
{
if (libssh2_init(0) != 0) {
qDebug() << "libssh2 initialization failed";
return false;
}
return true;
}
void SFTPClient::cleanupLibssh2()
{
if (sftpSession) {
libssh2_sftp_shutdown(sftpSession);
}
if (session) {
libssh2_session_disconnect(session, "Normal Shutdown");
libssh2_session_free(session);
}
if (sock != -1) {
close(sock);
}
libssh2_exit();
}
bool SFTPClient::connectToServer(const QString &hostname, const QString &username, const QString &password, int port)
{
struct sockaddr_in sin;
struct hostent *host;
// Resolve hostname
host = gethostbyname(hostname.toStdString().c_str());
if (!host) {
qDebug() << "Failed to resolve hostname";
return false;
}
// Create socket and connect
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = *(long *)(host->h_addr);
if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) {
qDebug() << "Failed to connect";
return false;
}
// Create session
session = libssh2_session_init();
if (!session) {
qDebug() << "Failed to create session";
return false;
}
// Handshake
if (libssh2_session_handshake(session, sock)) {
qDebug() << "Failed to establish SSH session";
return false;
}
// Authenticate
if (libssh2_userauth_password(session, username.toStdString().c_str(), password.toStdString().c_str())) {
qDebug() << "Authentication failed";
return false;
}
// Initialize SFTP session
sftpSession = libssh2_sftp_init(session);
if (!sftpSession) {
qDebug() << "Unable to initialize SFTP session";
return false;
}
qDebug() << "Connected and authenticated";
return true;
}
bool SFTPClient::uploadFile(const QString &localFilePath, const QString &remoteFilePath)
{
if (!sftpSession) {
qDebug() << "SFTP session not initialized";
return false;
}
QFile localFile(localFilePath);
if (!localFile.open(QIODevice::ReadOnly)) {
qDebug() << "Failed to open local file";
return false;
}
LIBSSH2_SFTP_HANDLE *sftpHandle = libssh2_sftp_open(sftpSession, remoteFilePath.toStdString().c_str(), LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR);
if (!sftpHandle) {
qDebug() << "Failed to open remote file";
return false;
}
char buffer[1024];
qint64 bytesRead;
qint64 totalBytes = localFile.size();
qint64 bytesSent = 0;
while ((bytesRead = localFile.read(buffer, sizeof(buffer))) > 0) {
char *ptr = buffer;
while (bytesRead > 0) {
int rc = libssh2_sftp_write(sftpHandle, ptr, bytesRead);
if (rc < 0) {
qDebug() << "Failed to write to remote file";
libssh2_sftp_close(sftpHandle);
return false;
}
ptr += rc;
bytesRead -= rc;
bytesSent += rc;
emit uploadProgress(bytesSent, totalBytes);
}
}
localFile.close();
libssh2_sftp_close(sftpHandle);
emit uploadFinished(true);
return true;
}
步骤3:将C++类与QML绑定
在 main.cpp
中,将 SFTPClient
类注册到 QML 中。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "sftpclient.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
SFTPClient sftpClient;
engine.rootContext()->setContextProperty("sftpClient", &sftpClient);
engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
步骤4:创建QML界面
创建一个简单的 QML 界面,允许用户输入服务器信息和文件路径,并进行文件传输。
Main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("SFTP Client")
Column {
anchors.centerIn: parent
spacing: 20
TextField {
id: hostnameField
width: parent.width - 40
placeholderText: "Hostname"
}
TextField {
id: usernameField
width: parent.width - 40
placeholderText: "Username"
}
TextField {
id: passwordField
width: parent.width - 40
placeholderText: "Password"
echoMode: TextInput.Password
}
TextField {
id: localFileField
width: parent.width - 40
placeholderText: "Local File Path"
}
TextField {
id: remoteFileField
width: parent.width - 40
placeholderText: "Remote File Path"
}
Button {
text: "Connect and Upload"
onClicked: {
sftpClient.connectToServer(hostnameField.text, usernameField.text, passwordField.text)
sftpClient.uploadFile(localFileField.text, remoteFileField.text)
}
}
ProgressBar {
id: progressBar
width: parent.width - 40
minimumValue: 0
maximumValue: 100
}
Text {
id: statusText
text: ""
}
}
Connections {
target: sftpClient
onUploadProgress: {
progressBar.value = (bytesSent / bytesTotal) * 100
}
onUploadFinished: {
statusText.text = success ? "Upload Finished" : "Upload Failed"
}
}
}
步骤5:配置项目文件
确保项目文件包含必要的模块和库链接。
project.pro
QT += qml quick gui network
CONFIG += c++11
SOURCES += main.cpp \
sftpclient.cpp
HEADERS += sftpclient.h
RESOURCES += qml.qrc
# Add libssh2 library
LIBS += -lssh2
总结
以上代码实现了一个简单的Qt QML应用,可以连接S