在 Python 中使用 SFTP 上传文件,但如果路径不存在则创建目录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14819681/
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 12:31:31  来源:igfitidea点击:

Upload files using SFTP in Python, but create directories if path doesn't exist

pythonsshsftpparamiko

提问by franzlorenzon

I want to upload a file on a remote server with Python. I'd like to check beforehand if the remote path is really existing, and if it isn't, to create it. In pseudocode:

我想用 Python 在远程服务器上上传一个文件。我想事先检查远程路径是否真的存在,如果不存在,则创建它。在伪代码中:

if(remote_path not exist):
    create_path(remote_path)
upload_file(local_file, remote_path)

I was thinking about executing a command in Paramiko to create the path (e.g. mkdir -p remote_path). I came up with this:

我正在考虑在 Paramiko 中执行一个命令来创建路径(例如mkdir -p remote_path)。我想出了这个:

# I didn't test this code

import paramiko, sys

ssh = paramiko.SSHClient()
ssh.connect(myhost, 22, myusername, mypassword)
ssh.exec_command('mkdir -p ' + remote_path)
ssh.close

transport = paramiko.Transport((myhost, 22))
transport.connect(username = myusername, password = mypassword)

sftp = paramiko.SFTPClient.from_transport(transport)
sftp.put(local_path, remote_path)
sftp.close()

transport.close()

But this solution doesn't sound good to me, because I close the connection and then reopen it again. Is there a better way to do it?

但是这个解决方案对我来说听起来不太好,因为我关闭了连接然后再次重新打开它。有没有更好的方法来做到这一点?

采纳答案by isedev

SFTP supports the usual FTP commands (chdir, mkdir, etc...), so use those:

SFTP 支持常用的 FTP 命令(chdir、mkdir 等...),因此请使用这些命令:

sftp = paramiko.SFTPClient.from_transport(transport)
try:
    sftp.chdir(remote_path)  # Test if remote_path exists
except IOError:
    sftp.mkdir(remote_path)  # Create remote_path
    sftp.chdir(remote_path)
sftp.put(local_path, '.')    # At this point, you are in remote_path in either case
sftp.close()


To fully emulate mkdir -p, you can work through remote_path recursively:

要完全模拟mkdir -p,您可以递归地处理 remote_path:

import os.path

def mkdir_p(sftp, remote_directory):
    """Change to this directory, recursively making new folders if needed.
    Returns True if any folders were created."""
    if remote_directory == '/':
        # absolute path so change directory to root
        sftp.chdir('/')
        return
    if remote_directory == '':
        # top-level relative directory must exist
        return
    try:
        sftp.chdir(remote_directory) # sub-directory exists
    except IOError:
        dirname, basename = os.path.split(remote_directory.rstrip('/'))
        mkdir_p(sftp, dirname) # make parent directories
        sftp.mkdir(basename) # sub-directory missing, so created it
        sftp.chdir(basename)
        return True

sftp = paramiko.SFTPClient.from_transport(transport)
mkdir_p(sftp, remote_path) 
sftp.put(local_path, '.')    # At this point, you are in remote_path
sftp.close()

Of course, if remote_path also contains a remote file name, then it needs to be split off, the directory being passed to mkdir_p and the filename used instead of '.' in sftp.put.

当然,如果remote_path 还包含远程文件名,则需要将其拆分,将目录传递给mkdir_p 并使用文件名代替'.'。在 sftp.put 中。

回答by gabhijit

Something simpler and slightly more readable too

一些更简单,更易读的东西

def mkdir_p(sftp, remote, is_dir=False):
    """
    emulates mkdir_p if required. 
    sftp - is a valid sftp object
    remote - remote path to create. 
    """
    dirs_ = []
    if is_dir:
        dir_ = remote
    else:
        dir_, basename = os.path.split(remote)
    while len(dir_) > 1:
        dirs_.append(dir_)
        dir_, _  = os.path.split(dir_)

    if len(dir_) == 1 and not dir_.startswith("/"): 
        dirs_.append(dir_) # For a remote path like y/x.txt 

    while len(dirs_):
        dir_ = dirs_.pop()
        try:
            sftp.stat(dir_)
        except:
            print "making ... dir",  dir_
            sftp.mkdir(dir_)

回答by Mickey Afaneh

Had to do this today. Here is how I did it.

今天不得不这样做。这是我如何做到的。

def mkdir_p(sftp, remote_directory):
    dir_path = str()
    for dir_folder in remote_directory.split("/"):
        if dir_folder == "":
            continue
        dir_path += r"/{0}".format(dir_folder)
        try:
            sftp.listdir(dir_path)
        except IOError:
            sftp.mkdir(dir_path)

回答by Daniil Burmashev

Assuming sftp operations are expensive, I would go with:

假设 sftp 操作很昂贵,我会选择:

def sftp_mkdir_p(sftp, remote_directory):
    dirs_exist = remote_directory.split('/')
    dirs_make = []
    # find level where dir doesn't exist
    while len(dirs_exist) > 0:
        try:
            sftp.listdir('/'.join(dirs_exist))
            break
        except IOError:
            value = dirs_exist.pop()
            if value == '':
                continue
            dirs_make.append(value)
        else:
            return False
    # ...and create dirs starting from that level
    for mdir in dirs_make[::-1]:
        dirs_exist.append(mdir)
        sftp.mkdir('/'.join(dirs_exist))```

回答by ritesh

you can use pysftp package:

您可以使用 pysftp 包:

import pysftp as sftp

#used to pypass key login
cnopts = sftp.CnOpts()
cnopts.hostkeys = None

srv = sftp.Connection(host="10.2.2.2",username="ritesh",password="ritesh",cnopts=cnopts)
srv.makedirs("a3/a2/a1", mode=777)  # will happily make all non-existing directories

you can check this link for more details: https://pysftp.readthedocs.io/en/release_0.2.9/cookbook.html#pysftp-connection-mkdir

您可以查看此链接以获取更多详细信息:https: //pysftp.readthedocs.io/en/release_0.2.9/cookbook.html#pysftp-connection-mkdir

回答by Mikhail Gerasimov

My version:

我的版本:

def is_sftp_dir_exists(sftp, path):
    try:
        sftp.stat(path)
        return True
    except Exception:
        return False


def create_sftp_dir(sftp, path):
    try:
        sftp.mkdir(path)
    except IOError as exc:
        if not is_sftp_dir_exists(sftp, path):
            raise exc


def create_sftp_dir_recursive(sftp, path):
    parts = deque(Path(path).parts)

    to_create = Path()
    while parts:
        to_create /= parts.popleft()
        create_sftp_dir(sftp, str(to_create))

We try mkdirwithout trying listdir/statfirst due to EAFP principle(it's also more performant to make one network request than several).

由于EAFP 原则,我们mkdir不尝试listdir/stat首先尝试(发出一个网络请求比发出多个网络请求的性能更高)。