=
# --*-- coding:utf-8 --*--
import os
import sys
import paramiko
class SSHManager:
def __init__(self, host, user, password):
self.host = host
self.user = user
self.password = password
self.ssh = None
self.sftp = None
self.sftp_connect()
self.ssh_connect()
def __del__(self):
if self.ssh:
self.ssh.close()
if self.sftp:
self.sftp.close()
def sftp_connect(self):
"""sftp连接"""
try:
transport = paramiko.Transport((self.host, 22))
transport.connect(username=self.user, password=self.password)
self.sftp = paramiko.SFTPClient.from_transport(transport)
except Exception as e:
raise RuntimeError("sftp connect failed {}".format(str(e)))
def ssh_connect(self):
"""ssh连接"""
try:
# 创建ssh对象
self.ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
self.ssh.connect(hostname=self.host,
port=22,
username=self.user,
password=self.password,
timeout=5)
except Exception:
raise RuntimeError("ssh connected to [host:{}, user:{}, password:{}] failed".format(self.host, self.user, self.password))
# def exec_command(self, cmd):
# """
# 通过ssh执行远程命令
# :param cmd:
# :return:
# """
# try:
# stdin, stdout, stderr = self.ssh.exec_command(cmd)
# return stdout.read().decode("utf-8")
# except Exception as e:
# raise RuntimeError('Exec command {} failed'.format(str(cmd)))
#
# def ssh_exec_cmd(self, cmd, path='~/1'):
# """
# 通过ssh连接到远程服务器,执行给定的命令
# :param cmd: 执行的命令
# :param path: 命令执行的目录
# :return: 返回结果
# """
# try:
# result = self.exec_command('cd ' + path + ';' + cmd)
# return result
# except Exception as e:
# raise RuntimeError('Exec command {} failed'.format(str(cmd)))
def list_directory(self,remote_directory_path):
"""
列出指定目录下的文件和目录
:param remote_path: 远程目录路径
:return:
"""
try:
# 进入该目录
self.sftp.chdir(remote_directory_path)
result = []
directory_data = []
file_data = []
cmd = 'ls --file-type ' + remote_directory_path
stdin, stdout, stderr = self.ssh.exec_command(cmd)
data = stdout.read().decode().split('\n')
for file_or_directory in data:
# 去除""
if file_or_directory :
# 如果后缀有/,则是目录
if file_or_directory[-1] == "/":
directory_data.append(file_or_directory[:-1])
# 否则为文件
else:
file_data.append(file_or_directory)
for directory in directory_data:
result.append({"name":directory,"type":"directory"})
for file in file_data:
result.append({"name": file,"type":"file"})
return result
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{}".format(remote_directory_path))
except Exception as e:
raise Exception("List directory {} failed:{}".format(remote_directory_path,str(e)))
def rename(self,before_remote_path,now_remote_path):
"""
重命名文件或者文件名
:param before_remote_path: 改名之前的远程文件或目录路径
:param now_remote_path: 改名之后的远程文件或目录路径
:return:
"""
try:
self.sftp.rename(before_remote_path,now_remote_path)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{}".format(before_remote_path))
except Exception as e:
raise Exception("Rename {} failed:{}".format(before_remote_path,str(e)))
def create_file(self,remote_file_path):
"""
创建文件
:param remote_file_path: 远程文件路径
:return:
"""
# TODO 待完成
def remove_file(self,remote_file_path):
"""
删除文件
:param remote_file_path: 远程文件路径
:return:
"""
try:
self.sftp.remove(remote_file_path)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{}".format(remote_file_path))
except OSError as e:
raise OSError("Currently deleting directory, should be a file:{}".format(remote_file_path))
except Exception as e:
raise Exception("Remove file {} failed:{}".format(remote_file_path,str(e)))
def create_directory(self,remote_directory_path):
"""
创建目录
:param remote_directory_path: 远程目录路径
:return:
"""
try:
self.sftp.mkdir(remote_directory_path)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{}".format(remote_directory_path))
except Exception as e:
raise Exception("Create directory {} failed:{}".format(remote_directory_path,str(e)))
def remove_directory(self,remote_directory_path):
"""
删除目录
:param remote_directory_path: 远程目录路径
:return:
"""
try:
self.sftp.rmdir(remote_directory_path)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{}".format(remote_directory_path))
except Exception as e:
raise Exception("Remove directory {} failed:{}".format(remote_directory_path,str(e)))
def download_file(self, local_file_path,remote_file_path):
"""
下载文件
注:
下载和上传文件的参数顺序相反,不一样
:param remote_file_path: 远程文件路径
:param local_file_path: 本地文件路径
:return:
"""
try:
self.sftp.get(remote_file_path,local_file_path)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{} or {}".format(local_file_path,remote_file_path))
except Exception as e:
raise Exception("Download file {} failed:{}".format(remote_file_path,str(e)))
def upload_file(self, local_file_path, remote_file_path):
"""
上传文件
:param local_file_path: 本地文件路径
:param remote_file_path: 远程文件路径
:return:
"""
try:
self.sftp.put(local_file_path, remote_file_path)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{} or {}".format(local_file_path, remote_file_path))
except Exception as e:
raise Exception("Upload file {} failed:{}".format(local_file_path,str(e)))
def download_directory(self,init_local_directory_path,init_remote_directory_path,current_remote_directory_path):
"""
下载文件夹
:param init_local_directory_path: 初始本地文件夹 (!!!初始本地文件夹让前端切割加上初始远程文件夹名称,最后结尾都不要\)
:param init_remote_directory_path: 初始远程文件夹
:param current_remote_directory_path: 当前远程文件夹
:return:
"""
try:
# 没有最后面的/ 加上
if len(init_local_directory_path) > 0 and init_local_directory_path[-1] != '/':
init_local_directory_path += '/'
if len(init_remote_directory_path) > 0 and init_remote_directory_path[-1] != '/':
init_remote_directory_path += '/'
if len(current_remote_directory_path) > 0 and current_remote_directory_path[-1] != '/':
current_remote_directory_path += '/'
list_dir = self.list_remote_dir(current_remote_directory_path)
# 如果是文件夹,且其下没有文件,那么也要创建该文件夹
if list_dir == [""]:
save_path = init_local_directory_path + current_remote_directory_path.split(init_remote_directory_path)[-1]
if not os.path.exists(save_path):
os.makedirs(save_path)
# 去掉'',linux获取目录文件,最后总是有一个''
nodes = list(item for item in list_dir if len(item) > 0)
for node in nodes:
# 如果该节点是文件夹,继续遍历
if node.endswith('/'):
self.download_directory(init_local_directory_path,init_remote_directory_path,current_remote_directory_path+ node)
else:
# 如果该节点是文件 则保存该文件
self.save_file(init_local_directory_path,init_remote_directory_path,current_remote_directory_path,node)
except Exception as e:
raise Exception("Download directory {} failed:{}".format(init_remote_directory_path, str(e)))
def list_remote_dir(self, remote_directory_path):
"""
获取远程服务器指定目录下的文件(仅供download_directory函数使用)
:param remote_directory_path: 远程路径
:return:
"""
cmd = 'ls --file-type ' + remote_directory_path
stdin, stdout, stderr = self.ssh.exec_command(cmd)
result = stdout.read()
return result.decode().split('\n')
def save_file(self,init_local_directory_path,init_remote_directory_path,current_remote_directory_path, name):
"""
保存文件(仅供download_directory函数使用)
:param init_local_directory_path: 初始保存的本地文件夹路径
:param init_remote_directory_path: 初始下载的远程文件夹路径
:param current_remote_directory_path: 当前远程文件夹路径
:param name: 当前远程文件夹下的文件名
:return:
"""
try:
# 本地保存路径
save_path = init_local_directory_path + current_remote_directory_path.split(init_remote_directory_path)[-1]
if not os.path.exists(save_path):
os.makedirs(save_path)
save_file_path = save_path + name
# 查看远程文件内容
cmd = 'cat ' + current_remote_directory_path + name
stdin, stdout, stderr = self.ssh.exec_command(cmd)
result = stdout.read()
if not result:
return
# 保存文件
with open(save_file_path, 'wb+') as fd:
fd.write(result)
except:
print('save path:' + save_file_path)
print("Unexpected error:", sys.exc_info()[0])
def upload_directory(self, local_directory_path, remote_directory_path):
"""
上传文件夹
:param local_directory_path: 本地文件夹路径
:param remote_directory_path: 远程文件夹路径
:return:
"""
try:
self.sftp.chdir(remote_directory_path)
project = local_directory_path.split("\\")[-1]
current_dir = ''
for root, dirs, files in os.walk(local_directory_path):
# 创建目录
create_remote_dirs = root.split(local_directory_path)[-1].split("\\") # 创建远程目录的后缀 \a\c\d
create_remote_dir_path = remote_directory_path + "/" + project # 创建远程目录路径
for create_remote_dir in create_remote_dirs:
if create_remote_dir:
create_remote_dir_path = create_remote_dir_path + "/" + create_remote_dir
# 进入目录,如果报错就创建该目录
try:
self.sftp.chdir(create_remote_dir_path)
except Exception as e:
self.sftp.mkdir(create_remote_dir_path)
# 上传文件
for file in files:
if root.split('\\')[-1] not in current_dir:
current_dir = '{}/{}'.format(current_dir, root.split('\\')[-1])
try:
# 切换到目录
self.sftp.chdir('{}{}'.format(remote_directory_path, current_dir))
# 上传文件
self.sftp.put(os.path.join(root, file), file)
except:
self.sftp.mkdir(root.split('\\')[-1])
self.sftp.chdir('{}{}'.format(remote_directory_path, current_dir))
self.sftp.put(os.path.join(root, file), file)
else:
self.sftp.chdir('{}{}'.format(remote_directory_path, current_dir))
self.sftp.put(os.path.join(root, file), file)
except FileNotFoundError as e:
raise FileNotFoundError("No such file or directory:{} or {}".format(local_directory_path, remote_directory_path))
except Exception as e:
raise Exception("Upload directory {} failed:{}".format(local_directory_path, str(e)))