Search code examples
pythonsftpparamikomkdirpythonista

SFTP Upload via Python and Pythonista with Paramiko. Can't create directory/subdirectory


I'm trying to upload an image to my uploads folder on my remote server. The folder structure is always uploads/year/month/ and I can't get paramiko to check if the folders exist and if not make them.

SSH connection is working, uploading a file is working too, but creating the subfolders in the uploads directory isn't working.

I came across what looked like the solution here. It's the same question I have, but I'm on iOS and use Pythonista. Option A: my code is plain wrong or Option B it's an iOS/Pythonista specific issue.

So, the code from the other thread (linked above) set a definition and runs a try/error loop to tests if the folders passed through it already exists and if not creates them. In my script below it's # Set Definition for "mkdir -p".

Calling it with the remoteFilePath

  1. Unnecessary because: ideally it should only test if datePath exists, since the remotePath definitely exists
  2. Likely problematic because: fileName is no path and will be put there by the next command.

I tried adjusting the script, but somehow I can't make it work.

I get errors no matter what I try:

  • with version 1: TypeError: mkdir_p() takes exactly 2 arguments (1 given)"
  • with version 2: AttributeError: 'tulpe' object has no attribute 'rfind'
  • with version 3: Exception: unknown type for (/home/userZ/Dropbox/uploads/year/month', 'test.png') type <type 'tuple'>

Here's a snippet of the relevant parts of the script (or a gist if you prefer the look of it):

# Set Variables
fileName = "temp.png"
remotePath = "/home/userZ/Dropbox/uploads/"
datePath = "year/month/"
remoteFilePath =  remotePath + datePath + fileName #

# Set Definition for "mkdir -p"
def mkdir_p(sftp,remote_directory):
    remote_dirname, basename = os.path.split(remote_directory)
    mkdir_p(os.path.dirname(remote_directory))
    try:
        sftp.chdir(name)
    except IOError:
        sftp.mkdir(name)
        sftp.chdir(name)

try:
    transport.connect(username = username, password = password)
    sftp = paramiko.SFTPClient.from_transport(transport)     # Start SFTP client

    # Try to make remote path - 3 Versions and all fail
    mkdir_p(sftp,remoteFilePath) # Version 1
    #mkdir_p(sftp, os.path.split(remoteFilePath)) # Version 2
    #sftp.mkdir(os.path.split(remoteFilePath)) # Version 3

    # Put file to remote
    sftp.put('temp.png', remoteFilePath)

    # Close connection
    finally:
        transport.close()
        sftp.close()

Any help is appreciated. (Careful: OP = Python noob). I rely on Paramiko because my shared host only supports SFTP. Otherwise I'd have gone with FTPlib.


Solution

  • CClauss got the answer and put it in the comment section of the gist linked above. I take no credit.

    This was his answer - in cause anyone else tries to emulate mkdir -p with paramiko, then here you go:

    My sense is that you need to: Try to mkdir on :/home/ Try to mkdir on :/home/userZ/ Try to mkdir on :/home/userZ/Dropbox/ Try to mkdir on :/home/userZ/Dropbox/uploads/ Try to mkdir on :/home/userZ/Dropbox/uploads/year/ Try to mkdir on :/home/userZ/Dropbox/uploads/year/month/ Then cd to /home/userZ/Dropbox/uploads/year/month/ Then copy your file

    Try this...

    # Slash '/' is hardcoded because ftp always uses slash
    def mk_each_dir(sftp, inRemoteDir):
        currentDir = '/'
        for dirElement in inRemoteDir.split('/'):
            if dirElement:
                currentDir += dirElement + '/'
                print('Try to mkdir on :' + currentDir)
                try:
                    sftp.mkdir(currentDir)
                except:
                    pass # fail silently if remote directory already exists
    
    # Set Variables
    fileName = "temp.png"
    remotePath = "/home/userZ/Dropbox/uploads/"
    datePath = "year/month/"
    remoteDirPath =  remotePath + datePath
    mk_each_dir(sftp, remoteDirPath)
    sftp.chdir(remoteDirPath)
    remoteFilePath =  remoteDirPath + fileName