Python 使用 Paramiko 进行目录传输

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4409502/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 15:37:39  来源:igfitidea点击:

Directory transfers with Paramiko

pythonsftpparamiko

提问by fixxxer

How do you use Paramiko to transfer complete directories? I'm trying to use:

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

sftp.put("/Folder1","/Folder2")

which is giving me this error -

这给了我这个错误 -

Error : [Errno 21] Is a directory

采纳答案by JimB

You'll need to do this just like you would locally with python (if you weren't using shutils).

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

Combine os.walk(), with sftp.mkdir()and sftp.put(). You may also want to check each file and directory with os.path.islink()depending on whether you want to resolve symlinks or not.

结合os.walk()sftp.mkdir()sftp.put()。您可能还想os.path.islink()根据是否要解析符号链接来检查每个文件和目录。

回答by Martijn

I don't think you can do that. Look up the documentation for os.walkand copy each file "manually".

我不认为你可以这样做。查找文档os.walk并“手动”复制每个文件。

回答by Martin Kosek

As far as I know, Paramiko does not support recursive file upload. However, I have found a solution for recursive upload using Paramiko here. Follows an excerpt of their recursive upload function:

据我所知,Paramiko 不支持递归文件上传。但是,我在这里找到了使用 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])

You may try to either use their function SCPClient.putinvoking the above function for recursive upload or implement it on your own.

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

回答by Andrey Gerzhov

You might replace sftp = self.client.open_sftp()with paramiko's one and get rid of libcloudhere.

你可以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)

回答by skoll

You can subclass paramiko.SFTPClient and add the following method to it:

您可以继承 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

To use it:

要使用它:

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()

回答by Zaffalon

Works for me doing something like this, all folder and files are copied to the remote server.

为我做这样的事情,所有文件夹和文件都被复制到远程服务器。

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))

回答by Alexandr Nikitin

Here's my piece of code:

这是我的一段代码:

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

回答by Raj401

This is my first StackOverflow answer. I had a task today which is similar to this. So, I tried to find a direct way to copy entire folder from windows to linux using python and paramiko. After a little research, I came up with this solution which works for smaller size folders with subfolders and files in it.

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

This solution first makes the zip file for the current folder (os.walk() is very much helpful here), then copies to destination server and unzip there.

此解决方案首先为当前文件夹制作 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')

回答by Michael Weatherby

This can all be done quite easily using just paramiko.

A high level summary of the code below is:
- connect to the SFTP (steps 1 to 3)
- specify your source and target folders. (step 4)
- copy them over one by one to wherever you like (I've sent them to /tmp/). (step 5)

这一切都可以使用 paramiko 轻松完成。

以下代码的高级摘要是:
- 连接到 SFTP(步骤 1 到 3)
- 指定您的源文件夹和目标文件夹。(第 4 步)
- 将它们一一复制到您喜欢的任何位置(我已将它们发送到 /tmp/)。(第 5 步)

import paramiko

# 1 - Open a transport
host="your-host-name"
port = port_number
transport = paramiko.Transport((host, port))

# 2 - Auth
password="sftp_password"
username="sftp_username"
transport.connect(username = username, password = password)

# 3 - Go!

sftp = paramiko.SFTPClient.from_transport(transport)

# 4 - Specify your source and target folders.
source_folder="some/folder/path/on/sftp"
inbound_files=sftp.listdir(source_folder)

# 5 - Download all files from that path
for file in inbound_files :
    filepath = source_folde+file
    localpath = "/tmp/"+file
    sftp.get(filepath, localpath)

回答by Martin Prikryl

Paramiko does not support directory transfers on its own. You have to implement it, as many existing answers here show.

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

Or you can use pysftp. It's a wrapper around Paramiko that has more Python-ish look and feel and supports recursive operations. See

或者你可以使用 pysftp。它是 Paramiko 的包装器,具有更多 Python 风格的外观和感觉并支持递归操作。看

Or you can just base your code on pysftp source code. Or see my answer to Python pysftp get_r from Linux works fine on Linux but not on Windows.

或者您可以将您的代码基于pysftp 源代码。或者看看我对Python pysftp get_r 的回答来自 Linux 在 Linux 上工作正常,但在 Windows 上不工作