paramiko批量上传下载sftp,解决访问windows系列sftp乱码问题

一、封装代码

import os
import paramiko
from stat import S_ISDIR
import shutil


class ConnectSftp(object):
    def __init__(self,ip,port,username,password):
        self.ip=ip
        self.port=port
        self.username=username
        self.password=password
        self.connect()

    def connect(self):
        self.transport = paramiko.Transport((self.ip, self.port))
        self.transport.connect(username=self.username, password=self.password)
        self.sftp = paramiko.SFTPClient.from_transport(self.transport)

    def close(self):
        self.transport.close()

    def isdir(self,path):
        try:
            return S_ISDIR(self.sftp.stat(path).st_mode)
        except IOError:
            return False

    def sftp_rm(self,path):
        files = self.sftp.listdir(path=path)
        for f in files:
            filepath = os.path.join(path, f)
            if self.isdir(filepath):
                self.sftp_rm(filepath)
            else:
                self.sftp.remove(filepath)
        self.sftp.rmdir(path)

    def local_to_sftp(self,local_dir_name,remote_dir_name,move_state=False):
        '''
        :param local_dir_name: 本地文件夹路径
        :param remote_dir_name: sftp文件夹路径
        :param move_state: 是否删除本地文件夹
        :return:
        '''
        if os.path.isdir(local_dir_name):
            # 文件夹,不能直接下载,需要继续循环
            self.check_sftp_dir(remote_dir_name)
            for remote_file_name in os.listdir(local_dir_name):
                sub_local = os.path.join(local_dir_name, remote_file_name)
                sub_local = sub_local.replace('\\', '/')
                sub_remote = os.path.join(remote_dir_name, remote_file_name)
                sub_remote = sub_remote.replace('\\', '/')
                print(sub_local, sub_remote)
                self.local_to_sftp(sub_local, sub_remote)
        else:
            # 文件,直接上传
            print('开始上传文件:' + local_dir_name)
            self.sftp.put(local_dir_name, remote_dir_name)
        if move_state:
            shutil.rmtree(local_dir_name)

    def sftp_to_local(self,remote_dir_name,local_dir_name,move_state=False):
        '''
        :param remote_dir_name: sftp文件夹路径
        :param local_dir_name: 本地文件夹路径
        :param move_state: 是否删除sftp文件夹
        :return:
        '''
        try:
            self.sftp.stat(remote_dir_name)
        except Exception:
            raise BaseException({"error":"sftp路径不存在"})
        remote_file = self.sftp.stat(remote_dir_name)
        if S_ISDIR(remote_file.st_mode):
            # 文件夹,不能直接下载,需要继续循环
            self.check_local_dir(local_dir_name)
            print('开始下载文件夹:' + remote_dir_name)
            for remote_file_name in self.sftp.listdir(remote_dir_name):
                sub_remote = os.path.join(remote_dir_name, remote_file_name)
                sub_remote = sub_remote.replace('\\', '/')
                sub_local = os.path.join(local_dir_name, remote_file_name)
                sub_local = sub_local.replace('\\', '/')
                self.sftp_to_local(sub_remote, sub_local)
        else:
            # 文件,直接下载
            print('开始下载文件:' + remote_dir_name)
            self.sftp.get(remote_dir_name, local_dir_name)
        if move_state:
            self.sftp_rm(remote_dir_name)


    def check_local_dir(self, local_dir_name):
        """本地文件夹是否存在,不存在则创建"""
        if not os.path.exists(local_dir_name):
            os.makedirs(local_dir_name)

    def check_sftp_dir(self, remote_dir_name):
        """sftp文件夹是否存在,不存在则创建"""
        try:
            self.sftp.stat(remote_dir_name)
        except IOError as e:
            self.sftp.mkdir(remote_dir_name)

    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.transport
        stdin, stdout, stderr = ssh.exec_command(command)
        print(stdout)
        result = stdout.read()
        return result


cs=ConnectSftp('ip',port,'name','pwd')
if __name__ == '__main__':
    cs.local_to_sftp(r"本地路径",'sftp路径',move_state=True)
    cs.sftp_to_local("sftp路径",r"本地路径",move_state=True)
    cs.close()

二、解决windows2008-sftp文件路径乱码

找到paramiko下的py3compat文件,将以下4个函数内的编码改成‘gbk’

    def b(s, encoding="gbk"):
        """cast unicode or bytes to bytes"""
        if isinstance(s, bytes):
            return s
        elif isinstance(s, str):
            return s.encode(encoding)
        else:
            raise TypeError("Expected unicode or bytes, got {!r}".format(s))

    def u(s, encoding="gbk"):
        """cast bytes or unicode to unicode"""
        if isinstance(s, bytes):
            return s.decode(encoding)
        elif isinstance(s, str):
            return s
        else:
            raise TypeError("Expected unicode or bytes, got {!r}".format(s))

def b(s, encoding="gbk"): # NOQA """cast unicode or bytes to bytes""" if isinstance(s, str): return s elif isinstance(s, unicode): # NOQA return s.encode(encoding) elif isinstance(s, buffer): # NOQA return s else: raise TypeError("Expected unicode or bytes, got {!r}".format(s)) def u(s, encoding="gbk"): # NOQA """cast bytes or unicode to unicode""" if isinstance(s, str): return s.decode(encoding) elif isinstance(s, unicode): # NOQA return s elif isinstance(s, buffer): # NOQA return s.decode(encoding) else: raise TypeError("Expected unicode or bytes, got {!r}".format(s))

猜你喜欢

转载自www.cnblogs.com/angelyan/p/12670444.html