Python 使用Paramiko进行目录传输

Python 使用Paramiko进行目录传输,python,sftp,paramiko,Python,Sftp,Paramiko,如何使用Paramiko传输完整的目录? 我正在尝试使用: sftp.put("/Folder1","/Folder2") 这给了我这个错误- 错误:[Errno 21]是一个目录 我认为你做不到。查阅os.walk的文档,然后“手动”复制每个文件。我认为你做不到。查阅os.walk的文档,并“手动”复制每个文件。据我所知,Paramiko不支持递归文件上载。然而,我发现了一个问题。下面是其递归上载函数的摘录: def _send_recursiv

如何使用Paramiko传输完整的目录? 我正在尝试使用:

sftp.put("/Folder1","/Folder2")
这给了我这个错误-

错误:[Errno 21]是一个目录


我认为你做不到。查阅
os.walk
的文档,然后“手动”复制每个文件。

我认为你做不到。查阅
os.walk的文档,并“手动”复制每个文件。

据我所知,Paramiko不支持递归文件上载。然而,我发现了一个问题。下面是其递归上载函数的摘录:

   def _send_recursive(self, files):
        for base in files:
            lastdir = base
            for root, dirs, fls in os.walk(base):
                # pop back out to the next dir in the walk
                while lastdir != os.path.commonprefix([lastdir, root]):
                    self._send_popd()
                    lastdir = os.path.split(lastdir)[0]
                self._send_pushd(root)
                lastdir = root
                self._send_files([os.path.join(root, f) for f in fls])

您可以尝试使用其函数
SCPClient.put
调用上述函数进行递归上传,也可以自己实现。

据我所知,Paramiko不支持递归文件上传。然而,我发现了一个问题。下面是其递归上载函数的摘录:

   def _send_recursive(self, files):
        for base in files:
            lastdir = base
            for root, dirs, fls in os.walk(base):
                # pop back out to the next dir in the walk
                while lastdir != os.path.commonprefix([lastdir, root]):
                    self._send_popd()
                    lastdir = os.path.split(lastdir)[0]
                self._send_pushd(root)
                lastdir = root
                self._send_files([os.path.join(root, f) for f in fls])

您可以尝试使用它们的函数
SCPClient.put
调用上述函数进行递归上载,也可以自己实现它。

您需要像本地使用python一样执行此操作(如果您没有使用shutils)


os.walk()
sftp.mkdir()
sftp.put()
组合在一起。您可能还需要使用
os.path.islink()
检查每个文件和目录,具体取决于您是否要解析符号链接。

您需要像在本地使用python一样执行此操作(如果您没有使用shutils)


os.walk()
sftp.mkdir()
sftp.put()
组合在一起。您可能还需要使用
os.path.islink()
检查每个文件和目录,具体取决于您是否要解析符号链接。

您可以将
sftp=self.client.open_sftp()
替换为paramiko的文件和目录,并在此处删除
libcloud

import os.path
from stat import S_ISDIR
from libcloud.compute.ssh import SSHClient
from paramiko.sftp import SFTPError

class CloudSSHClient(SSHClient):


    @staticmethod
    def normalize_dirpath(dirpath):
        while dirpath.endswith("/"):
            dirpath = dirpath[:-1]
        return dirpath


    def mkdir(self, sftp, remotepath, mode=0777, intermediate=False):
        remotepath = self.normalize_dirpath(remotepath)
        if intermediate:
            try:
                sftp.mkdir(remotepath, mode=mode)
            except IOError, e:
                self.mkdir(sftp, remotepath.rsplit("/", 1)[0], mode=mode,
                           intermediate=True)
                return sftp.mkdir(remotepath, mode=mode)
        else:
            sftp.mkdir(remotepath, mode=mode)


    def put_dir_recursively(self,  localpath, remotepath, preserve_perm=True):
        "upload local directory to remote recursively"

        assert remotepath.startswith("/"), "%s must be absolute path" % remotepath

        # normalize
        localpath = self.normalize_dirpath(localpath)
        remotepath = self.normalize_dirpath(remotepath)

        sftp = self.client.open_sftp()

        try:
            sftp.chdir(remotepath)
            localsuffix = localpath.rsplit("/", 1)[1]
            remotesuffix = remotepath.rsplit("/", 1)[1]
            if localsuffix != remotesuffix:
                remotepath = os.path.join(remotepath, localsuffix)
        except IOError, e:
            pass

        for root, dirs, fls in os.walk(localpath):
            prefix = os.path.commonprefix([localpath, root])
            suffix = root.split(prefix, 1)[1]
            if suffix.startswith("/"):
                suffix = suffix[1:]

            remroot = os.path.join(remotepath, suffix)

            try:
                sftp.chdir(remroot)
            except IOError, e:
                if preserve_perm:
                    mode = os.stat(root).st_mode & 0777
                else:
                    mode = 0777
                self.mkdir(sftp, remroot, mode=mode, intermediate=True)
                sftp.chdir(remroot)

            for f in fls:
                remfile = os.path.join(remroot, f)
                localfile = os.path.join(root, f)
                sftp.put(localfile, remfile)
                if preserve_perm:
                    sftp.chmod(remfile, os.stat(localfile).st_mode & 0777)

您可以将
sftp=self.client.open_sftp()
替换为paramiko的一个,并在此处删除
libcloud

import os.path
from stat import S_ISDIR
from libcloud.compute.ssh import SSHClient
from paramiko.sftp import SFTPError

class CloudSSHClient(SSHClient):


    @staticmethod
    def normalize_dirpath(dirpath):
        while dirpath.endswith("/"):
            dirpath = dirpath[:-1]
        return dirpath


    def mkdir(self, sftp, remotepath, mode=0777, intermediate=False):
        remotepath = self.normalize_dirpath(remotepath)
        if intermediate:
            try:
                sftp.mkdir(remotepath, mode=mode)
            except IOError, e:
                self.mkdir(sftp, remotepath.rsplit("/", 1)[0], mode=mode,
                           intermediate=True)
                return sftp.mkdir(remotepath, mode=mode)
        else:
            sftp.mkdir(remotepath, mode=mode)


    def put_dir_recursively(self,  localpath, remotepath, preserve_perm=True):
        "upload local directory to remote recursively"

        assert remotepath.startswith("/"), "%s must be absolute path" % remotepath

        # normalize
        localpath = self.normalize_dirpath(localpath)
        remotepath = self.normalize_dirpath(remotepath)

        sftp = self.client.open_sftp()

        try:
            sftp.chdir(remotepath)
            localsuffix = localpath.rsplit("/", 1)[1]
            remotesuffix = remotepath.rsplit("/", 1)[1]
            if localsuffix != remotesuffix:
                remotepath = os.path.join(remotepath, localsuffix)
        except IOError, e:
            pass

        for root, dirs, fls in os.walk(localpath):
            prefix = os.path.commonprefix([localpath, root])
            suffix = root.split(prefix, 1)[1]
            if suffix.startswith("/"):
                suffix = suffix[1:]

            remroot = os.path.join(remotepath, suffix)

            try:
                sftp.chdir(remroot)
            except IOError, e:
                if preserve_perm:
                    mode = os.stat(root).st_mode & 0777
                else:
                    mode = 0777
                self.mkdir(sftp, remroot, mode=mode, intermediate=True)
                sftp.chdir(remroot)

            for f in fls:
                remfile = os.path.join(remroot, f)
                localfile = os.path.join(root, f)
                sftp.put(localfile, remfile)
                if preserve_perm:
                    sftp.chmod(remfile, os.stat(localfile).st_mode & 0777)

您可以将paramiko.SFTPClient子类化并向其添加以下方法:

import paramiko
import os

class MySFTPClient(paramiko.SFTPClient):
    def put_dir(self, source, target):
        ''' Uploads the contents of the source directory to the target path. The
            target directory needs to exists. All subdirectories in source are 
            created under target.
        '''
        for item in os.listdir(source):
            if os.path.isfile(os.path.join(source, item)):
                self.put(os.path.join(source, item), '%s/%s' % (target, item))
            else:
                self.mkdir('%s/%s' % (target, item), ignore_existing=True)
                self.put_dir(os.path.join(source, item), '%s/%s' % (target, item))

    def mkdir(self, path, mode=511, ignore_existing=False):
        ''' Augments mkdir by adding an option to not fail if the folder exists  '''
        try:
            super(MySFTPClient, self).mkdir(path, mode)
        except IOError:
            if ignore_existing:
                pass
            else:
                raise
要使用它:

transport = paramiko.Transport((HOST, PORT))
transport.connect(username=USERNAME, password=PASSWORD)
sftp = MySFTPClient.from_transport(transport)
sftp.mkdir(target_path, ignore_existing=True)
sftp.put_dir(source_path, target_path)
sftp.close()

您可以将paramiko.SFTPClient子类化并向其添加以下方法:

import paramiko
import os

class MySFTPClient(paramiko.SFTPClient):
    def put_dir(self, source, target):
        ''' Uploads the contents of the source directory to the target path. The
            target directory needs to exists. All subdirectories in source are 
            created under target.
        '''
        for item in os.listdir(source):
            if os.path.isfile(os.path.join(source, item)):
                self.put(os.path.join(source, item), '%s/%s' % (target, item))
            else:
                self.mkdir('%s/%s' % (target, item), ignore_existing=True)
                self.put_dir(os.path.join(source, item), '%s/%s' % (target, item))

    def mkdir(self, path, mode=511, ignore_existing=False):
        ''' Augments mkdir by adding an option to not fail if the folder exists  '''
        try:
            super(MySFTPClient, self).mkdir(path, mode)
        except IOError:
            if ignore_existing:
                pass
            else:
                raise
要使用它:

transport = paramiko.Transport((HOST, PORT))
transport.connect(username=USERNAME, password=PASSWORD)
sftp = MySFTPClient.from_transport(transport)
sftp.mkdir(target_path, ignore_existing=True)
sftp.put_dir(source_path, target_path)
sftp.close()

对于我来说,这样做是可行的,所有文件夹和文件都复制到远程服务器

parent = os.path.expanduser("~")
for dirpath, dirnames, filenames in os.walk(parent):
    remote_path = os.path.join(remote_location, dirpath[len(parent)+1:])
        try:
            ftp.listdir(remote_path)
        except IOError:
            ftp.mkdir(remote_path)

        for filename in filenames:
            ftp.put(os.path.join(dirpath, filename), os.path.join(remote_path, filename))

对于我来说,这样做是可行的,所有文件夹和文件都复制到远程服务器

parent = os.path.expanduser("~")
for dirpath, dirnames, filenames in os.walk(parent):
    remote_path = os.path.join(remote_location, dirpath[len(parent)+1:])
        try:
            ftp.listdir(remote_path)
        except IOError:
            ftp.mkdir(remote_path)

        for filename in filenames:
            ftp.put(os.path.join(dirpath, filename), os.path.join(remote_path, filename))

以下是我的代码:

import errno
import os
import stat

def download_files(sftp_client, remote_dir, local_dir):
    if not exists_remote(sftp_client, remote_dir):
        return

    if not os.path.exists(local_dir):
        os.mkdir(local_dir)

    for filename in sftp_client.listdir(remote_dir):
        if stat.S_ISDIR(sftp_client.stat(remote_dir + filename).st_mode):
            # uses '/' path delimiter for remote server
            download_files(sftp_client, remote_dir + filename + '/', os.path.join(local_dir, filename))
        else:
            if not os.path.isfile(os.path.join(local_dir, filename)):
                sftp_client.get(remote_dir + filename, os.path.join(local_dir, filename))


def exists_remote(sftp_client, path):
    try:
        sftp_client.stat(path)
    except IOError, e:
        if e.errno == errno.ENOENT:
            return False
        raise
    else:
        return True

以下是我的代码:

import errno
import os
import stat

def download_files(sftp_client, remote_dir, local_dir):
    if not exists_remote(sftp_client, remote_dir):
        return

    if not os.path.exists(local_dir):
        os.mkdir(local_dir)

    for filename in sftp_client.listdir(remote_dir):
        if stat.S_ISDIR(sftp_client.stat(remote_dir + filename).st_mode):
            # uses '/' path delimiter for remote server
            download_files(sftp_client, remote_dir + filename + '/', os.path.join(local_dir, filename))
        else:
            if not os.path.isfile(os.path.join(local_dir, filename)):
                sftp_client.get(remote_dir + filename, os.path.join(local_dir, filename))


def exists_remote(sftp_client, path):
    try:
        sftp_client.stat(path)
    except IOError, e:
        if e.errno == errno.ENOENT:
            return False
        raise
    else:
        return True

这是我的第一个答案。我今天有一个类似的任务。所以,我试图找到一种直接的方法,使用python和paramiko将整个文件夹从windows复制到linux。经过一点研究,我提出了这个解决方案,它适用于包含子文件夹和文件的较小大小的文件夹

此解决方案首先生成当前文件夹的zip文件(os.walk()在这里非常有用),然后复制到目标服务器并在那里解压缩

zipHere = zipfile.ZipFile("file_to_copy.zip", "w")

for root, folders, files in os.walk(FILE_TO_COPY_PATH):
    for file in files:
        zipHere.write(os.path.join(root, file), arcname=os.path.join(os.path.relpath(root, os.path.dirname(FILE_TO_COPY_PATH)), file))
    for folder in folders:
        zipHere.write(os.path.join(root, folder), arcname=os.path.join(os.path.relpath(root, os.path.dirname(FILE_TO_COPY_PATH)), folder))
zipHere.close()

# sftp is the paramiko.SFTPClient connection
sftp.put('local_zip_file_location','remote_zip_file_location')

# telnet_conn is the telnetlib.Telnet connection
telnet_conn.write('cd cd_to_zip_file_location')
telnet_conn.write('unzip -o file_to_copy.zip')

这是我的第一个答案。我今天有一个类似的任务。所以,我试图找到一种直接的方法,使用python和paramiko将整个文件夹从windows复制到linux。经过一点研究,我提出了这个解决方案,它适用于包含子文件夹和文件的较小大小的文件夹

此解决方案首先生成当前文件夹的zip文件(os.walk()在这里非常有用),然后复制到目标服务器并在那里解压缩

zipHere = zipfile.ZipFile("file_to_copy.zip", "w")

for root, folders, files in os.walk(FILE_TO_COPY_PATH):
    for file in files:
        zipHere.write(os.path.join(root, file), arcname=os.path.join(os.path.relpath(root, os.path.dirname(FILE_TO_COPY_PATH)), file))
    for folder in folders:
        zipHere.write(os.path.join(root, folder), arcname=os.path.join(os.path.relpath(root, os.path.dirname(FILE_TO_COPY_PATH)), folder))
zipHere.close()

# sftp is the paramiko.SFTPClient connection
sftp.put('local_zip_file_location','remote_zip_file_location')

# telnet_conn is the telnetlib.Telnet connection
telnet_conn.write('cd cd_to_zip_file_location')
telnet_conn.write('unzip -o file_to_copy.zip')

这一切都可以很容易地做到只用paramiko。

以下代码的概要如下:
-连接到SFTP(步骤1至3)
-指定源文件夹和目标文件夹。(步骤4)
-将它们逐个复制到您喜欢的任何地方(我已将它们发送到/tmp/)。(步骤5)


这一切都可以很容易地做到只用paramiko。

以下代码的概要如下:
-连接到SFTP(步骤1至3)
-指定源文件夹和目标文件夹。(步骤4)
-将它们逐个复制到您喜欢的任何地方(我已将它们发送到/tmp/)。(步骤5)


Paramiko本身不支持目录传输。正如许多现有答案所示,您必须实现它

或者您可以使用pysftp。它是一个围绕Paramiko的包装器,具有更多类似Python的外观,并支持递归操作。看

或者,您也可以只基于您的代码。完整的独立便携式Paramiko纯代码请参见我的答案:


正如我上面的回答所示,如果您在Windows上,您实际上必须使用自己的代码,因为PySTFP在Windows上不工作。

Paramiko本身不支持目录传输。正如许多现有答案所示,您必须实现它

或者您可以使用pysftp。它是一个围绕Paramiko的包装器,具有更多类似Python的外观,并支持递归操作。看

或者,您也可以只基于您的代码。完整的独立便携式Paramiko纯代码请参见我的答案:


正如我上面的回答所示,如果您在Windows上,您实际上必须使用自己的代码,因为PySTFP在Windows上不起作用。

我的答案与上面的答案类似,只需列出一个列表,然后逐个传输即可

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='11.11.11.1111', username='root', password='********', port=22)
sftp_client = ssh.open_sftp()
source_folder = '/var/ftp/file_pass'
local_folder = 'C:/temp/file_pass'
inbound_files = sftp_client.listdir(source_folder)
print(inbound_files)

for ele in inbound_files:
    try:
        path_from = source_folder + '/' + ele
        path_to = local_folder + '/'+ ele
        sftp_client.get(path_from, path_to)
    except:
        print(ele)

sftp_client.close()
ssh.close()

我的答案和上面的一样,只是列一个清单,然后一个接一个地转移

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='11.11.11.1111', username='root', password='********', port=22)
sftp_client = ssh.open_sftp()
source_folder = '/var/ftp/file_pass'
local_folder = 'C:/temp/file_pass'
inbound_files = sftp_client.listdir(source_folder)
print(inbound_files)

for ele in inbound_files:
    try:
        path_from = source_folder + '/' + ele
        path_to = local_folder + '/'+ ele
        sftp_client.get(path_from, path_to)
    except:
        print(ele)

sftp_client.close()
ssh.close()

walk()是正确的方法,但不要完全复制它,因为它以特定于SCP的方式处理事情。SFTP的工作原理有点不同(免责声明,我写了那个代码)@martinkosek-我喜欢你的答案,但看起来你与解决方案的链接断了。你能编辑和修复吗?谢谢。@RobertMS-好的,我看到Python模块被删除了。在这种情况下,我认为JimB的解决方案应该是实现目标的最佳组合os.walk()、sftp.mkdir()和sftp.put()。os.walk()是实现这一目标的正确方法,但不要完全复制它,因为它以特定于SCP的方式处理事情。SFTP的工作原理稍有不同(discl