【SSH、Python】SCPClientWrapper-用于客户端上传和下载文件 —可显示ip,端口 和 传输百分比

25 阅读6分钟

CountServerChildFiles-从本地利用ssh统计服务器上某个文件夹中的子文件数量

import subprocess


class CountServerChildFiles:
    """ 从本地利用ssh统计服务器上某个文件夹中的子文件数量 """

    def get_file_count_via_ssh(self, hostname, port, username, password=None, folder_path=''):
        """ 
        获取远程服务器上文件夹内的文件总数
        返回file_count:int """
        # 构建SSH命令
        
        command = [
            'ssh',
            f'-p {port}',
            f'{username}@{hostname}',
            f'sh -c "find {folder_path} -type f | wc -l"'
        ]

        # 如果使用密码认证,则需要使用pexpect或类似库来处理交互
        # 如果使用密钥认证,则不需要密码

        # 执行命令并获取输出
        # subprocess.run()执行完后立刻关闭连接
        result = subprocess.run(command, stdout=subprocess.PIPE, text=True)

        # 检查命令是否成功执行
        if result.returncode == 0:
            # 提取文件总数
            file_count = int(result.stdout.strip())
            return file_count
        else:
            # 命令执行失败,处理错误
            raise Exception("无法通过SSH检索文件数")

    
    
    def get_file_list_via_ssh(self,hostname, port, username, password=None, folder_path='.'):  
        """  
        获取远程服务器上文件夹内的文件列表,例如:['xxx.txt','lin.py','font.conf'](不包括子文件夹)  
        返回file_list:list  
        """  
        # 构建SSH命令,使用'find'命令来搜索文件并过滤掉目录  
        command = [  
            'ssh',  
            f'-p {port}',  
            f'{username}@{hostname}',  
            f'sh -c "find {folder_path} -type f"'  
        ]  
    
        # 执行命令并获取输出,显式设置编码为utf-8  
        result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8')  
    
        # 检查命令是否成功执行  
        if result.returncode == 0:  
            # 提取文件列表,移除可能的空行和换行符  
            file_list = [line.strip() for line in result.stdout.split('\n') if line.strip()]  
            return file_list  
        else:  
            # 命令执行失败,处理错误  
            error_message = result.stderr.strip()  
            raise Exception(f"无法通过SSH检索文件列表: {error_message}")  
    
    # 获取要传输的文件列表,以计算总文件数  
    def get_file_list(self, folder, recursive=False):  
        """ 统计本地中文件夹内的文件总数,返回文件名列表,例如:[['xxx.txt','lin.py','font.conf']](不包括文件夹) """
        import os  
        file_list = []  
        for root, dirs, files in os.walk(folder):  
            if not recursive and root != folder:  
                break  
            file_list.extend(files)  
        # 返回数量可以用len(file_list)
        return file_list 
    
""" 测试用例
if __name__ == '__main__':
    c = CountServerChildFiles()
    # result = c.get_file_list_via_ssh(hostname='192.168.1.6',port=22,username='root',password='',folder_path='/home/lin/桌面/mycode')
    result = c.get_file_list(r'D:\PYproject\PyScript',recursive=True) # recursive就是是否递归到子文件夹中计数,一般都是Ture
    print(result)
"""

SCPClientWrapper-用于客户端上传和下载文件 —可显示ip,端口 和 传输百分比

from paramiko import SSHClient
from scp import SCPClient
import sys
from utils.CountServerChildFiles import CountServerChildFiles


class SCPClientWrapper:
    """ 
    基于SCPClient
    SCPClient封装包
    用于客户端上传和下载文件
    可显示ip,端口 和 传输百分比
    """
    # 传输时显示每个文件的百分比,并且不断输出
    # def upload_file(self, hostname, local_folder, remote_username, remote_path, recursive=False):
    #     """
    #     上传文件
    #     hostname:主机IP地址
    #     resursive|Boolean:是否递归目录,如果是上传一个文件以及他里面的子文件,就需要设置为Ture,默认False(需要大写)
    #     local_folder:本地文件或文件夹的路径
    #     remote_path:远程文件夹

    #     """
    #     ssh = SSHClient()
    #     ssh.load_system_host_keys()
    #     ssh.connect(hostname=hostname, username=remote_username)

    #     #定义打印文件当前完成百分比的进度回调
    #     def progress(filename, size, sent):
    #         sys.stdout.write("%s's progress: %.2f%%   \r" %
    #                          (filename, float(sent)/float(size)*100))

    #     # SCPCLient将paramiko传输和进度回调作为其参数。
    #     scp = SCPClient(ssh.get_transport(), progress=progress)

    #     #您还可以使用progress4,它添加了第四个参数来跟踪IP和端口
    #     #有助于多线程跟踪源代码
    #     def progress4(filename, size, sent, peername):
    #         # sys.stdout.write("(%s:%s) %s's progress: %.2f%%   \r" % (peername[0], peername[1], filename, float(sent)/float(size)*100))
    #         print(f"({peername[0]}:{peername[1]}) {filename} progress: {float(sent)/float(size)*100:.2f}%")
    #     scp = SCPClient(ssh.get_transport(), progress4=progress4)

    #     scp.put(files=local_folder, remote_path=remote_path, recursive=recursive)
    #     #现在应该正在打印您的put函数的当前进度。

    #     scp.close()
# ----------------------------------------------------------------
    # 也会显示百分比,并且是整个文件的传输百分比,只在终端显示一行,不会连续输出
    def upload_file(self, hostname, port, local_folder, remote_username, remote_path, recursive=False):
        """  
        上传文件  
        :param hostname: 主机IP地址  
        :param local_folder: 本地文件或文件夹的路径  
        :param remote_username: 远程用户名  
        :param remote_path: 远程文件夹  
        :param recursive: 是否递归目录, 默认False  
        """
        ssh = SSHClient()
        ssh.load_system_host_keys()
        ssh.connect(hostname=hostname, username=remote_username)

        # 全局变量,用于追踪传输进度
        total_files = 0
        transferred_files = 0

        # 定义打印文件传输进度的回调
        def progress_callback(filename, size, sent):
            nonlocal total_files, transferred_files

            # 文件传输开始时,增加已传输文件数
            if sent == 0:
                transferred_files += 1
                return  # 避免在文件传输开始时打印进度

            # 计算并显示整体进度
            percentage = (transferred_files / total_files) * 100
            sys.stdout.write(f"\033[33m({hostname}:{port})传输进度: {percentage:.2f}%   \r\033[0m")
            sys.stdout.flush()  # 确保实时输出

        # 获取要传输的文件列表,以计算总文件数
        def get_file_list(folder, recursive=False):
            import os
            file_list = []
            for root, dirs, files in os.walk(folder):
                if not recursive and root != folder:
                    break
                file_list.extend(files)
            return file_list

        # 获取要传输的文件总数
        files_to_transfer = get_file_list(local_folder, recursive=recursive)
        total_files = len(files_to_transfer)

        # 创建SCPClient实例并传入进度回调函数
        scp = SCPClient(ssh.get_transport(), progress=progress_callback)

        
        # 上传文件或目录
        scp.put(files=local_folder, remote_path=remote_path, recursive=recursive)

        # 关闭SCPClient连接
        scp.close()

        # 文件传输全部完成后,打印最终进度
        print(f"\033[32m({hostname}:{port})传输完成: 100.00%\033[0m")

    def download_file(self, hostname, local_path, remote_username, remote_folder, recursive=True, port='22', password=None):
        """  
        下载文件  
        :param hostname: 主机IP地址  
        :param local_folder: 本地文件或文件夹的路径  
        :param remote_username: 远程用户名  
        :param remote_path: 远程文件夹  
        :param recursive: 是否递归目录, 默认Ture, 
        因为下载的有文件有文件夹,所以必须递归,故设置resursive为Ture
        """
        ssh = SSHClient()
        ssh.load_system_host_keys()
        ssh.connect(hostname=hostname, username=remote_username)

        # 全局变量,用于追踪传输进度
        total_files = 0
        transferred_files = 0

        # 定义打印文件传输进度的回调
        def progress_callback(filename, size, sent):
            nonlocal total_files, transferred_files

            # 文件传输开始时,增加已传输文件数
            if sent == 0:
                transferred_files += 1
                return  # 避免在文件传输开始时打印进度

            # 计算并显示整体进度
            percentage = (transferred_files / total_files) * 100
            sys.stdout.write(f"\033[33m ({hostname}:{port})传输进度: {percentage:.2f}%   \r\033[0m")
            sys.stdout.flush()  # 确保实时输出

        # 获取要传输的文件列表,以计算总文件数
        files_to_transfer = CountServerChildFiles().get_file_list_via_ssh(hostname=hostname,
                                                                          port=port, username=remote_username, password=password, folder_path=remote_folder)
        # files_to_transfer = get_file_list(folder=remote_folder, recursive=recursive)
        total_files = len(files_to_transfer)

        # 创建SCPClient实例并传入进度回调函数
        scp = SCPClient(ssh.get_transport(), progress=progress_callback)

        # 下载文件或目录,
        # 因为下载的有文件有文件夹,所以必须递归,故设置resursive为Ture
        scp.get(local_path=local_path,
                remote_path=remote_folder, recursive=recursive)

        # 关闭SCPClient连接
        scp.close()

        # 文件传输全部完成后,打印最终进度
        print(f"\033[32m({hostname}:{port})传输完成: 100.00%\033[0m")


""" 使用示例:
# if __name__ == '__main__':
#     scp = SCPClientWrapper()
#     scp.upload_file(hostname='192.168.1.6',port='22',local_folder='D:\软件\AAA',remote_path='/home/lin/桌面/mycode/',remote_username='root',recursive=True) 
"""