Je veux télécharger un fichier sur un serveur distant avec Python. J'aimerais vérifier au préalable si le chemin distant existe vraiment, et si ce n'est pas le cas, pour le créer. En pseudocode:
if(remote_path not exist):
create_path(remote_path)
upload_file(local_file, remote_path)
Je pensais à l'exécution d'une commande dans Paramiko pour créer le chemin (par exemple mkdir -p remote_path
). Je suis venu avec ceci:
# 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()
Mais cette solution ne me semble pas satisfaisante, car je ferme la connexion puis la rouvre à nouveau. Y a-t-il une meilleure façon de le faire?
SFTP supporte les commandes FTP habituelles (chdir, mkdir, etc ...), utilisez donc celles-ci:
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()
Pour émuler entièrement mkdir -p
, vous pouvez travailler de façon récursive via 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()
Bien sûr, si chemin_distant contient également un nom de fichier distant, il doit être séparé, le répertoire étant passé à mkdir_p et le nom de fichier utilisé à la place de '.'. dans sftp.put.
Quelque chose de plus simple et légèrement plus lisible aussi
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_)
Je devais le faire aujourd'hui. Voici comment je l'ai fait.
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)
Paramiko contient une fonction mkdir:
En supposant que les opérations sftp sont chères, .__ Je voudrais avec:
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))```