paramiko模块实现文件夹的上传及下载

paramiko模块的简单介绍及使用

Posted by rpl on April 19, 2019

paramiko模块基于ssh协议,实现了对远程服务器的连接登录,命令执行和文件操作等功能。

下面的SSH类, 基于paramiko模块 封装实现了连接登录, 文件上传与下载, 以及文件夹的上传与下载等功能。

SSH() 的初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import os
import stat
import paramiko
import traceback

class SSH(object):

    def __init__(self, ip, port=22, username=None, password=None, timeout=30):
        self.ip = ip  # ssh远程连接的服务器ip
        self.port = 22  # ssh的端口一般默认是22,
        self.username = username  # 服务器用户名
        self.password = password  # 密码
        self.timeout = timeout  # 连接超时
		
		# paramiko.SSHClient() 创建一个ssh对象,用于ssh登录以及执行操作
        self.ssh = paramiko.SSHClient()
        
		# paramiko.Transport()创建一个文件传输对象,用于实现文件的传输
        self.t = paramiko.Transport(sock=(self.ip, self.port))

连接登录

1
2
3
4
5
6
7
8
9
   def connect(self):
        try:
            self._key_connect()   # 密钥登录
        except:
            print('ssh key connect failed, trying to password connect...')
            try:
                self._password_connect()  # 密码登录
            except:
                print('ssh password connect faild!')
  • 密钥登录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	def _key_connect(self):
	
		# 获取本地的私钥文件 一般是在 ~/.ssh/id_rsa
		self.pkey = paramiko.RSAKey.from_private_key_file('~/.ssh/id_rsa', ) 
		
		# 获取本地的knows_hosts 文件,在不允许连接非kown_hosts文件中的主机时,使用它
		# self.ssh.load_system_host_keys() 
		
		# 允许连接非know_hosts文件中的主机
		ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
		
		# 建立登录连接
		self.ssh.connect(hostname=self.ip, port=22, username=self.username, pkey=self.pkey)
		
		# 建立文件传输的连接
		self.t.connect(username=self.username, pkey=self.pkey)

  • 密码登录
1
2
3
4
5
    def _password_connect(self):
        
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(hostname=self.ip, port=22, username=self.username, password=self.password)
        self.t.connect(username=self.username, password=self.password)  # sptf 远程传输的连接

断开连接

1
2
3
    def close(self):
        self.t.close()  # 断开文件传输的连接
        self.ssh.close()  # 断开ssh连接

执行命令

1
2
3
4
5
6
7
8
    def execute_cmd(self, cmd):

        stdin, stdout, stderr = self.ssh.exec_command(cmd)

        res, err = stdout.read(), stderr.read()
        result = res if res else err

        return result.decode()

文件的上传和下载

1
2
3
4
5
6
7
8
9
10
11
    # 从远程服务器获取文件到本地
    def sftp_get(self, remotefile, localfile):

        sftp = paramiko.SFTPClient.from_transport(self.t)
        sftp.get(remotefile, localfile)

    # 从本地上传文件到远程服务器
    def sftp_put(self, localfile, remotefile):

        sftp = paramiko.SFTPClient.from_transport(self.t)
        sftp.put(localfile, remotefile)

本地文件夹的上传到远程服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
	# 递归遍历远程服务器指定目录下的所有文件
    def _get_all_files_in_remote_dir(self, sftp, remote_dir):
        all_files = list()
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        files = sftp.listdir_attr(remote_dir)
        for file in files:
            filename = remote_dir + '/' + file.filename

            if stat.S_ISDIR(file.st_mode):  # 如果是文件夹的话递归处理
                all_files.extend(self._get_all_files_in_remote_dir(sftp, filename))
            else:
                all_files.append(filename)

        return all_files

	# 本地文件夹的上传到远程服务器
    def sftp_get_dir(self, remote_dir, local_dir):
        try:
        
            sftp = paramiko.SFTPClient.from_transport(self.t)

            all_files = self._get_all_files_in_remote_dir(sftp, remote_dir)

            for file in all_files:
            
                local_filename = file.replace(remote_dir, local_dir)
                local_filepath = os.path.dirname(local_filename)

                if not os.path.exists(local_filepath):
                    os.makedirs(local_filepath)

                sftp.get(file, local_filename)

        except:
            print('ssh get dir from master failed.')
            print(traceback.format_exc())  # 具体报错信息

远程服务器文件夹下载到本地

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    # 递归遍历本地服务器指定目录下的所有文件
    def _get_all_files_in_local_dir(self, local_dir):
        all_files = list()

        for root, dirs, files in os.walk(local_dir, topdown=True):
            for file in files:
                filename = os.path.join(root, file)
                all_files.append(filename)

        return all_files

    def sftp_put_dir(self, local_dir, remote_dir):
        try:
            sftp = paramiko.SFTPClient.from_transport(self.t)

            if remote_dir[-1] == "/":
                remote_dir = remote_dir[0:-1]

            all_files = self._get_all_files_in_local_dir(local_dir)
            for file in all_files:

                remote_filename = file.replace(local_dir, remote_dir)
                remote_path = os.path.dirname(remote_filename)

                try:
                    sftp.stat(remote_path)
                except:
                    os.popen('mkdir -p %s' % remote_path)

                sftp.put(file, remote_filename)

        except:
            print('ssh get dir from master failed.')
            print(traceback.format_exc())

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import os
import stat
import paramiko
import traceback


'''
使用paramiko类实现ssh的连接登陆,以及远程文件的上传与下载, 基本远程命令的实现等

'''


class SSH(object):

    def __init__(self, ip, port=22, username=None, password=None, timeout=30):
        self.ip = ip
        self.port = port
        self.username = username
        self.password = password
        self.timeout = timeout

        self.ssh = paramiko.SSHClient()

        self.t = paramiko.Transport(sock=(self.ip, self.port))


    def _password_connect(self):

        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(hostname=self.ip, port=22, username=self.username, password=self.password)

        self.t.connect(username=self.username, password=self.password)  # sptf 远程传输的连接

    def _key_connect(self):
        # 建立连接
        self.pkey = paramiko.RSAKey.from_private_key_file('/home/roo/.ssh/id_rsa', )
        # self.ssh.load_system_host_keys()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(hostname=self.ip, port=22, username=self.username, pkey=self.pkey)

        self.t.connect(username=self.username, pkey=self.pkey)

    def connect(self):
        try:
            self._key_connect()
        except:
            print('ssh key connect failed, trying to password connect...')
            try:
                self._password_connect()
            except:
                print('ssh password connect faild!')

    def close(self):
        self.t.close()
        self.ssh.close()
        
    def execute_cmd(self, cmd):

        stdin, stdout, stderr = self.ssh.exec_command(cmd)

        res, err = stdout.read(), stderr.read()
        result = res if res else err

        return result.decode()

    # 从远程服务器获取文件到本地
    def _sftp_get(self, remotefile, localfile):

        sftp = paramiko.SFTPClient.from_transport(self.t)
        sftp.get(remotefile, localfile)

    # 从本地上传文件到远程服务器
    def _sftp_put(self, localfile, remotefile):

        sftp = paramiko.SFTPClient.from_transport(self.t)
        sftp.put(localfile, remotefile)

    # 递归遍历远程服务器指定目录下的所有文件
    def _get_all_files_in_remote_dir(self, sftp, remote_dir):
        all_files = list()
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        files = sftp.listdir_attr(remote_dir)
        for file in files:
            filename = remote_dir + '/' + file.filename

            if stat.S_ISDIR(file.st_mode):  # 如果是文件夹的话递归处理
                all_files.extend(self._get_all_files_in_remote_dir(sftp, filename))
            else:
                all_files.append(filename)

        return all_files

    def sftp_get_dir(self, remote_dir, local_dir):
        try:

            sftp = paramiko.SFTPClient.from_transport(self.t)

            all_files = self._get_all_files_in_remote_dir(sftp, remote_dir)

            for file in all_files:

                local_filename = file.replace(remote_dir, local_dir)
                local_filepath = os.path.dirname(local_filename)

                if not os.path.exists(local_filepath):
                    os.makedirs(local_filepath)

                sftp.get(file, local_filename)
        except:
            print('ssh get dir from master failed.')
            print(traceback.format_exc())

    # 递归遍历本地服务器指定目录下的所有文件
    def _get_all_files_in_local_dir(self, local_dir):
        all_files = list()

        for root, dirs, files in os.walk(local_dir, topdown=True):
            for file in files:
                filename = os.path.join(root, file)
                all_files.append(filename)

        return all_files

    def sftp_put_dir(self, local_dir, remote_dir):
        try:
            sftp = paramiko.SFTPClient.from_transport(self.t)

            if remote_dir[-1] == "/":
                remote_dir = remote_dir[0:-1]

            all_files = self._get_all_files_in_local_dir(local_dir)
            for file in all_files:

                remote_filename = file.replace(local_dir, remote_dir)
                remote_path = os.path.dirname(remote_filename)

                try:
                    sftp.stat(remote_path)
                except:
                    os.popen('mkdir -p %s' % remote_path)

                sftp.put(file, remote_filename)

        except:
            print('ssh get dir from master failed.')
            print(traceback.format_exc())


if __name__ == "__main__":

    ssh = SSH(ip='192.168.0.10', username='rpl', password='12345678')  # 创建一个ssh类对象

	ssh.connect()  # 连接远程服务器
	
	cmd = 'ls -lh'
    ssh.execute_cmd(cmd)  # 执行命令

	remotefile, local_file = 'xxx', 'xxx'
	ssh.sftp_get(remotefile, local_file)  # 下载文件
	
	remotedir, localdir = 'xxx', 'xxx'
	ssh.sftp_put_dir(localdir, remotedir)  # 上传文件夹
	ssh.close()