Automating file transfer via FTP over TLS using Python
By Jean-Claude Colette
Mar 16, 2017
Description:
Automating file transfer via FTP over TLS using Python
Tags: Python
In this article, we will see how to transfer files from a local computer to a remote server via a FTPS connection.
The goal is to automate file transfers with scripts in Python on Windows.

Introduction
On Windows, there is no basic software to perform secure FTP transfers. There is, however, paid or free solutions.
The language Python 3.5 has a library ftplib.py for FTP with TLS connections.
But it is necessary to modify the ftplib.py file, if we want to write a file on an IIS server.
Indeed, the storbinary
function hangs up unexpectedly when run.
So, let's start by placing it in comment lines 512 and 513 of ftplib.py.
511: # shutdown ssl layer
512: # if _SSLSocket is not None and isinstance(conn, _SSLSocket):
513: # conn.unwrap()
the problem has already been reported by http://www.sami-lehtinen.net/blog/python-32-ms-ftps-ssl-tls-lockup-fix
Scripts in Python
This first script to display the list of files in a directory of the remote server.
from ftplib import FTP_TLS
import sys, os
destdir="/rootdir/blog"
ftps = FTP_TLS('kodfor.com')
ftps.set_debuglevel(1)
ftps.set_pasv(False)
ftps.connect(port=21, timeout=80)
ftps.login('mylogin', 'password')
ftps.prot_p()
ftps.ccc()
ftps.cwd(destdir)
ftps.pwd()
ftps.retrlines('LIST')
ftps.close()
This second script copy recursively the content of a local directory named flac to a directory on the remote server.
from ftplib import FTP_TLS
import sys, os
import os.path
root="C:\\Users\\kodfor\\Documents\\mypython\\flac"
destdir="/rootdir/flac"
ftps = FTP_TLS('kodfor.com')
ftps.set_debuglevel(1)
ftps.set_pasv(False)
ftps.connect(port=21, timeout=80)
ftps.login('mylogin', 'password')
ftps.prot_p()
ftps.ccc()
try:
ftps.cwd(destdir)
except Exception:
ftps.mkd(destdir)
for (dir, _, files) in os.walk(root):
newdir=destdir+dir[len(root):len(dir)].replace("\\","/")
try:
ftps.cwd(newdir)
except Exception:
ftps.mkd(newdir)
for f in files:
file = open(os.path.join(dir, f),'rb')
ftps.storbinary('STOR '+f, file,blocksize=8192)
file.close()
ftps.close()
This last script removes a directory named blog with all its contents.
from ftplib import FTP_TLS
import sys, os
import os.path
destdir="/rootdir"
rootdir = "blog"
def crossdel(ftps, curdir):
ftps.cwd(curdir)
l=ftps.nlst()
lines = []
ftps.retrlines('LIST', lines.append)
dirlist = []
filelist = []
for i in range(len(l)):
if lines[i].find("<DIR>")>0:
dirlist.append(l[i])
else:
filelist.append(l[i])
for f in filelist:
ftps.delete(f)
for d in dirlist:
print('dir',d)
crossdel(ftps, d)
p = ftps.pwd()
pl = p.split('/')
dd = pl[len(pl)-1]
ftps.sendcmd('CDUP')
if p!=destdir:
ftps.rmd(dd)
ftps = FTP_TLS('kodfor.com')
ftps.set_debuglevel(1)
ftps.set_pasv(False)
ftps.connect(port=21, timeout=80)
ftps.login('mylogin', 'password')
ftps.prot_p()
ftps.ccc()
ftps.cwd(rootdir)
crossdel(ftps, rootdir)
ftps.close()
You still must change the connections settings and the different names of the directories.
Now write a python script to run multiple commands on the remote server in the same session.
We want to be able to call this script by passing a string containing commands, so that it executes them.
For that we pick most of the FTP commands. Commands can be separated in the string by a semicolon ";" or a carriage return \n.
The commands are:
- cwd Change working directory on the remote server
- dele Delete file
- mkd Make directory
- ren Rename file
- rmd Remove a directory
- stor store the data as a binary file at the server site
- nop no operation
ftpcon.py
from ftplib import FTP_TLS
import sys, os
import re
def cwd(ftps, destdir, none)
ftps.cwd(destdir)
def stor(ftps, srcfile, fileNameOnServer)
ext = os.path.splitext(srcfile)[1]
if ext in (.txt, .htm, .html)
file = open(srcfile)
ftps.storlines(STOR + fileNameOnServer, file)
else
file = open(srcfile,'rb')
ftps.storbinary('STOR ' + fileNameOnServer, file, blocksize=8192)
file.close()
def rmd(ftps, dirname, other)
ftps.rmd(dirname)
def mkd(ftps, dirname, other)
ftps.mkd(dirname)
def dele(ftps, file, other)
ftps.delete(file)
def ren(ftps, fromname, toname)
ftps.rename(fromname, toname)
def nop(ftps, any, other)
None
functions = [cwd, dele, mkd, ren, rmd, stor, nop, nop]
keywords = [cwd, dele, mkd, ren, rmd, stor, nop, ]
script = sys.argv[1]
#print(script)
script += ';'
pat = re.compile([nr;]+)
script = re.sub(pat, ';', script)
pat = re.compile([s]+)
script = re.sub(pat, ' ', script)
instruction_list = script.split(';')
#print(instruction_list)
destdir=rootdir
ftps = FTP_TLS('xxxxxxxxxxxxx.1and1-data.host')
ftps.set_debuglevel(0)
ftps.set_pasv(False)
ftps.connect(port=21, timeout=80)
ftps.login('mylogin', 'password')
ftps.prot_p()
ftps.ccc()
ftps.cwd(destdir)
ftps.pwd()
#ftps.retrlines('LIST')
for i in instruction_list
op = i.split(' ')
instr = no
arg1 =
arg2 =
l = len(op)
if l 0
instr = keywords.index(op[0])
if l 1
arg1 = op[1]
if l 2
arg2 = op[2]
#print( + keywords[instr] +' ' + arg1 + ' ' + arg2)
try
functions[instr](ftps, arg1, arg2)
except Exception
print(Error in %s % keywords[instr])
#ftps.retrlines('LIST')
ftps.close()
Try to call this script from the PowerShell:
test.ps1
$commands = @"
cwd Images;
dele circle.png
stor C:\Users\TheUser\Documents\kodfor\Images\circle.png circle.png;
cwd ..
cwd Algorithm
dele image001.gif;
dele image002.jpg;
dele image003.jpg;
ren image004.jpg image001.jpg
"@
echo $commands
C:\Python\Python36-32\python.exe ftpcon.py $commands
