SSH连接SFTP传输:如何使用libssh库在windows环境下进行(文件、文件夹)传输到远端服务器

由于windows上的编译器一般都是没有libssh库的,所以如何我们想要使用libssh库那么我们将会使用cmake来编译libssh官网给出的源代码

libssh库下载地址:https://www.libssh.org/files/

配置环境

我们在编译libssh库之前需要先配置一些环境:

  • a) 安装 Visual Studio 或者 MinGW
  • b) 安装OpenSSL http://slproweb.com/products/Win32OpenSSL.htmlC:\Program Files
  • c) 安装zlib http://zlib.net/zlib128-dll.zipC:\Program Files
  • d) 安装CMake http://cmake.org/cmake/resources/software.html

有些库需要将其.dll加入环境变量,具体可自行探索

cmake编译libssh

解压压缩包,进入目录,在当前页面右键打开终端

执行以下命令:

bash 复制代码
mkdir build
cd build
cmake ..

如果出现一些报错很可能是配置环境的问题,可以观察报错信息逐一解决

打开libssh.sln项目(使用cmake编译的编译器与打开的编译器需一致)

ssh设为启动项目

Ctrl + B生成解决项目,生成dlllib文件

如何使用生成的dll与lib文件

配置lib 方法一

打开vs工程 -》 项目 -》 属性

  1. 将头文件也就是进入include下的路径添加进入附加包含目录
    G:\xxx\libssh-0.10.0\include
  2. .lib文件所在目录添加进入附加库目录
    G:\xxx\libssh-0.10.0\build\src\Debug
  3. ssh.lib添加进入附加依赖项

配置lib 方法二

ssh.lib文件复制到当前项目所在路径下

ssh.lib导入项目即可,同理头文件也可直接复制进入项目路径

  • 如果是第一种方法,头文件引入可以#include <libssh/libssh.h> #include <libssh/sftp.h>
  • 如果是第二种方法,头文件引入必须#include "libssh/libssh.h" #include "libssh/sftp.h"

如果编译报错缺少这个文件libssh_version.h

这个文件在这个路径下G:\xxx\libssh-0.10.0\build\include\libssh,可以直接复制到libssh/下使用

配置dll方法一 将dll配置进入环境变量

点击此电脑 -》右键属性 -》 点击高级系统设置

dll文件所在路径添加进入环境变量
G:\xxx\libssh-0.10.0\build\src\Debug

配置dll方法二 在编译过后将dll直接与可执行文件.exe文件放在同一目录下

要使得用libssh库的项目能通过编译,需要将头文件以及.lib配置进入项目
如果出现__imp__ xxx类型的链接错误很有很可能是当前项目是X86环境,而libssh库是X64的库,需要将vs执行环境改为X64

传输文件、文件夹代码(适配windows)

c 复制代码
int sftp_normal_upload(ssh_session session, sftp_session sftp, const char* local_file_path, const char* remote_file_path)
{
    sftp_file file = sftp_open(sftp, remote_file_path, O_WRONLY | O_CREAT, 0666);
    if (!file) {
        fprintf(stderr, "Failed to open remote file: %s:sftp_get_error(%d)\n", remote_file_path, sftp_get_error(sftp));
        fprintf(stderr, "Can't create directory: %s\n", ssh_get_error(session));
        return -1;
    }

    FILE* local_file = fopen(local_file_path, "rb");
    if (!local_file) {
        fprintf(stderr, "Failed to open local file: %s\n", local_file_path);
        sftp_close(file);
        return -1;
    }

    // 上传文件内容
    char buffer[1024];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), local_file)) > 0) {
        sftp_write(file, buffer, bytes_read);
    }

    fclose(local_file);
    sftp_close(file);
    return 1;
}



int sftp_recursive_upload(ssh_session session, sftp_session sftp, const char* local_path, const char* remote_path) {

    // 计算转换后的宽字符所需缓冲区大小
    int bufferSize_local = MultiByteToWideChar(CP_ACP, 0, local_path, -1, NULL, 0);
    int bufferSize_remote = MultiByteToWideChar(CP_ACP, 0, remote_path, -1, NULL, 0);

    // 分配缓冲区
    wchar_t* local_path_to_wchar = (wchar_t*)malloc(bufferSize_local * sizeof(wchar_t));
    wchar_t* remote_path_to_wchar = (wchar_t*)malloc(bufferSize_remote * sizeof(wchar_t));
    if (local_path_to_wchar == NULL || local_path_to_wchar == NULL) {
        fprintf(stderr, "Failed to memory\n");
        // 处理内存分配失败的情况
        return -1;
    }

    // 执行转换
    MultiByteToWideChar(CP_ACP, 0, local_path, -1, local_path_to_wchar, bufferSize_local);
    MultiByteToWideChar(CP_ACP, 0, remote_path, -1, remote_path_to_wchar, bufferSize_remote);

    //给local_path追加通配符
    const wchar_t* str = L"\\*";
    wchar_t local_current[MAX_PATH];

    if (wcslen(local_path_to_wchar) + 3 >= MAX_PATH - 1)
    {
        fprintf(stderr, "Failed to strcat wildcard character\n");
        return -1;
    }
    // 将str1复制到result中
    wcscpy(local_current, local_path_to_wchar);
    // 追加str2到result中
    wcscat(local_current, str);


    // 打开本地目录
    WIN32_FIND_DATA FindFileData;
    HANDLE hFind = INVALID_HANDLE_VALUE;
    hFind = FindFirstFile(local_current, &FindFileData);
    if (hFind == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "Failed to open local directory: %s\n", local_current);
        return -1;
    }

    // 创建服务器目录
    sftp_mkdir(sftp, remote_path, 0777);

    // 遍历本地目录项
    do {
        if (wcscmp(FindFileData.cFileName, L".") != 0 && wcscmp(FindFileData.cFileName, L"..") != 0) {
            // 构造全路径
            wchar_t local_file_path[MAX_PATH];
            _snwprintf(local_file_path, sizeof(local_file_path), L"%s\\%s", local_path_to_wchar, FindFileData.cFileName);
            wchar_t remote_file_path[MAX_PATH];
            _snwprintf(remote_file_path, sizeof(remote_file_path), L"%s/%s", remote_path_to_wchar, FindFileData.cFileName);

            // 将宽字符字符串转换为多字节字符字符串
            char local_next[MAX_PATH];
            char remote_next[MAX_PATH];
            if (wcstombs(local_next, local_file_path, sizeof(local_next)) == (size_t)-1 ||
                wcstombs(remote_next, remote_file_path, sizeof(remote_next)) == (size_t)-1) {
                fprintf(stderr, "wchar_t conversion to char failed\n");
                return -1;
            }

            // 如果本地条目是一个目录,递归上传它
            if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                sftp_recursive_upload(session, sftp, local_next, remote_next);
            }
            else { // 如果本地条目是一个普通文件,上传它
                sftp_normal_upload(session, sftp, local_next, remote_next);
            }
        }
    } while (FindNextFile(hFind, &FindFileData) != 0);

    free(local_path_to_wchar);
    free(remote_path_to_wchar);
    FindClose(hFind);
    return 1;
}


ssh_session ssh_create(const char* target_host, const char* target_username)
{
    ssh_session session;

    // 连接SSH会话
    session = ssh_new();
    if (!session) {
        fprintf(stderr, "Failed to create SSH session\n");
        return NULL;
    }

    ssh_options_set(session, SSH_OPTIONS_HOST, target_host);
    ssh_options_set(session, SSH_OPTIONS_USER, target_username);

    if (ssh_connect(session) != SSH_OK) {
        fprintf(stderr, "Failed to connect to SSH session: %s\n", ssh_get_error(session));
        ssh_free(session);
        return NULL;
    }
    return session;
}

int sftp_run(ssh_session session, const char* local_file_dir, const char* target_path)
{
    sftp_session sftp;

    // 打开SFTP通道
    sftp = sftp_new(session);
    if (!sftp) {
        fprintf(stderr, "Failed to create SFTP session\n");
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

    if (sftp_init(sftp) != SSH_OK) {
        fprintf(stderr, "Failed to initialize SFTP session\n");
        sftp_free(sftp);
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }


    // 得到当前 文件/目录 所在路径
    char local_dir[4096];
    getcwd(local_dir, sizeof(local_dir));
    int len = strlen(local_dir);
    if ((len + strlen(local_file_dir) + 2) >= 4095) {
        perror("Local filename is too long");
        return -1;
    }

    local_dir[len] = '\\';
    local_dir[len + 1] = '\0';
    strcat(local_dir, local_file_dir);

    struct _stat file_stat;

    // 获取文件的详细信息
    if (_stat(local_dir, &file_stat) != 0) {
        perror("stat");
        return -1;
    }

    // 判断文件类型

    //普通文件
    if ((file_stat.st_mode & _S_IFMT) == _S_IFREG) {
        sftp_normal_upload(session, sftp, local_dir, target_path);
    }
    //目录文件
    else if ((file_stat.st_mode & _S_IFMT) == _S_IFDIR) {
        // 递归上传本地目录到远程目录
        if (sftp_recursive_upload(session, sftp, local_dir, target_path) != 1) {
            fprintf(stderr, "Failed to recursively upload directory\n");
            sftp_free(sftp);
            ssh_disconnect(session);
            ssh_free(session);
            return -1;
        }
    }
    else {
        printf("%s 既不是普通文件,也不是目录,无法上传\n", local_dir);
    }

    // 释放
    sftp_free(sftp);
    ssh_disconnect(session);
    ssh_free(session);

    return 1;
}

int ssh_sftp_transmit_password(const char* target_host, const char* target_username,
    const char* target_password, const char* local_file_dir, const char* target_path)
{
    // ssh连接建立
    ssh_session session = ssh_create(target_host, target_username);
    if (session == NULL)return -1;

    // 身份验证 - 密码
    if (ssh_userauth_password(session, NULL, target_password) != SSH_AUTH_SUCCESS) {
        fprintf(stderr, "Failed to authenticate with password\n");
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

    // 传输
    int re = sftp_run(session, local_file_dir, target_path);

    return re;
}


int ssh_sftp_transmit_publickey(const char* target_host, const char* target_username,
    const char* local_file_dir, const char* target_path)
{
    // ssh连接建立
    ssh_session session = ssh_create(target_host, target_username);
    if (session == NULL)return -1;

    // 身份验证 - 密钥
    if (ssh_userauth_publickey_auto(session, NULL, NULL) != SSH_AUTH_SUCCESS) {
        fprintf(stderr, "Authentication failed: %s\n", ssh_get_error(session));
        ssh_disconnect(session);
        ssh_free(session);
        return -1;
    }

    // 传输
    int re = sftp_run(session, local_file_dir, target_path);

    return re;
}
相关推荐
星释1 小时前
鸿蒙Flutter三方库适配指南: 05.使用Windows搭建开发环境
windows·flutter·harmonyos
R0ot2 小时前
面向安全增强的SSH版本升级实战指南
运维·安全·ssh
Gold Steps.2 小时前
常见的Linux发行版升级openSSH10.+
linux·运维·服务器·安全·ssh
炒茄子11 小时前
Windows:解决电脑开机解锁后黑屏但鼠标可见可移动的问题
windows·计算机外设
luyun02020218 小时前
流批了,pdf批量转excel
windows·pdf·excel·figma
vortex520 小时前
在 Windows 系统中安装 Oracle、SQL Server(MSSQL)和 MySQL
windows·oracle·sqlserver
路由侠内网穿透20 小时前
本地部署开源物联网平台 ThingsBoard 并实现外部访问( Windows 版本)
运维·服务器·windows·物联网·开源
Mr.Lu ‍1 天前
Windows开发,制作开发软件安装程序(二)
windows
skywalk81631 天前
windows装wsl ubuntu24.04 ,里面装qemu ,然后装mac os (windows也可以直接qemu安装macos)(未实践)
windows·ubuntu·macos·qemu
电脑小白技术1 天前
u盘安装系统提示“windows无法安装到这个磁盘,选中的磁盘具有gpt分区表”解决方法
windows·gpt·windows无法安装到磁盘