Please bear with me. Its only been a week I started on python. Heres the problem: I want to connect to a FTP Server. Presuming the file structure on my ftp and local directory is same. I want my python program to do the following:
1> On running the program, it should upload all the files that are NOT on the server but on my
local(Upload just the missing files-Not replacing all).
Say, I add a new directory or a new file, it should be uploaded on the server as it is.
2> It should then check for modified times of both the files on the local and the server and inform which is the latest.
NOw, I have made two programs:
1>One program that will upload ALL files from local to server, as it is. I would rather it check for missing files and then uipload just the missing files folders. NOt replace all.
2> Second program will list all files from the local, using os.walk and it will upload all files on the server without creating the correct directory structure.
All get copied to the root of server. Then it also checks for modified times.
I am in amess right now trying to JOIN these two modules into one perfect that does all I want it to.Anyone who could actually look at these codes and try joining them to what I wanana do, would be perfect. Sorry for being such a pain!!
PS:I might have not done everything the easy way!!
Code NO 1:
import sys
from ftplib import FTP
import os
def uploadDir(localdir,ftp):
"""
for each directory in an entire tree
upload simple files, recur into subdirectories
"""
localfiles = os.listdir(localdir)
for localname in localfiles:
localpath = os.path.join(localdir, localname)
print ('uploading', localpath, 'to', localname)
if not os.path.isdir(localpath):
os.chdir(localdir)
ftp.storbinary('STOR '+localname, open(localname, 'rb'))
else:
try:
ftp.mkd(localname)
print ('directory created')
except:
print ('directory not created')
ftp.cwd(localname) # change remote dir
uploadDir(localpath,ftp) # upload local subdir
ftp.cwd('..') # change back up
print ('directory exited')
def Connect(path):
ftp = FTP("127.0.0.1")
print ('Logging in.')
ftp.login('User', 'Pass')
uploadDir(path,ftp)
Connect("C:\\temp\\NVIDIA\\Test")
Code No2:
import os,pytz,smtplib
import time
from ftplib import FTP
from datetime import datetime,timedelta
from email.mime.text import MIMEText
def Connect_FTP(fileName,pathToFile): path from the local path
(dir,file) = os.path.split(fileName)
fo = open("D:\log.txt", "a+") # LOgging Important Events
os.chdir(dir)
ftp = FTP("127.0.0.1")
print ('Logging in.')
ftp.login('User', 'Pass')
l=dir.split(pathToFile)
print(l[1])
if file in ftp.nlst():
print("file2Check:"+file)
fo.write(str(datetime.now())+": File is in the Server. Checking the Versions....\n")
Check_Latest(file,fileName,ftp,fo)
else:
print("File is not on the Server. So it is being uploaded!!!")
fo.write(str(datetime.now())+": File is NOT in the Server. It is being Uploaded NOW\n")
ftp.storbinary('STOR '+file, open(file, 'rb'))
print("The End")
def Check_Latest(file2,path_on_local,ftp,fo): # Function to check the latest file, USING the "MOdified TIme"
LOcalFile = os.path.getmtime(path_on_local)
dloc=datetime.fromtimestamp(LOcalFile).strftime("%d %m %Y %H:%M:%S")
print("Local File:"+dloc)
localTimestamp=str(time.mktime(datetime.strptime(dloc, "%d %m %Y %H:%M:%S").timetuple())) # Timestamp to compare LOcalTime
modifiedTime = ftp.sendcmd('MDTM '+file2) # Using MDTM command to get the MOdified time.
IST = pytz.timezone('Asia/Kolkata')
ServTime=datetime.strptime(modifiedTime[4:], "%Y%m%d%H%M%S")
tz = pytz.timezone("UTC")
ServTime = tz.localize(ServTime)
j=str(ServTime.astimezone(IST)) # Changing TimeZone
g=datetime.strptime(j[:-6],"%Y-%m-%d %H:%M:%S")
ServerTime = g.strftime('%d %m %Y %H:%M:%S')
serverTimestamp=str(time.mktime(datetime.strptime(ServerTime, "%d %m %Y %H:%M:%S").timetuple())) # Timestamp to compare Servertime
print("ServerFile:"+ServerTime)
if serverTimestamp > localTimestamp:
print ("Old Version on The Client. You need to update your copy of the file")
fo.write(str(datetime.now())+": Old Version of the file "+file2+" on the Client\n")
return
else:
print ("The File on the Server is Outdated!!!New COpy Uploaded")
fo.write(str(datetime.now())+": The server has an outdated file: "+file2+". An email is being generated\n")
ftp.storbinary('STOR '+file2, open(file2, 'rb'))
def Connect(pathToFile):
for path, subdirs, files in os.walk(pathToFile):
for name in files:
j=os.path.join(path, name)
print(j)
Connect_FTP(j,pathToFile)
Connect("C:\temp\NVIDIA\Test")
May be this script will be useful.
Related
I am trying to delete files older than x days using below code from FTP server.
#!/usr/bin/env python
from ftplib import FTP
import time
import sys
import os
ftp_user = sys.argv[1]
ftp_pwd = sys.argv[2]
host = sys.argv[3]
def remove_files(days, dir_path):
ftp = FTP(host)
ftp.login(ftp_user, ftp_pwd)
ftp.cwd(dir_path)
now = time.time()
for file in ftp.nlst(dir_path):
print("filename:", file)
#file_path =os.path.join(dir_path, f)
if not os.path.isfile(file):
continue
if os.stat(file).st_mtime < now - days * 86400:
ftp.delete(file)
print("Deleted ", file)
I am not getting any error but files are not deleted. I think os module commands are not working in FTP server. Is there any alternative to delete the files from FTP older than x days. Basically I calling this script from ansible to automate the process.
Indeed, os.path doesn't work with files over ftp.
You can use mlsd in the following manner:
ftp.mlsd(facts=["Modify"])
It'll return a list of tuples, each looking like:
('favicon.ico', {'modify': '20110616024613'})
(the first item is the file name, the second is a dictionary with the last modified time).
To get more information about each file - for example, the file's type, use:
ftp.mlsd(facts=["Modify", "Type"])
This results in data like:
('.manifest.full', {'modify': '20200423140048', 'type': 'file'})
('14.04', {'modify': '20140327184332', 'type': 'OS.unix=symlink'})
('.pool', {'modify': '20200423134557', 'type': 'dir'})
I have a requirement where I have to pull the latest files from an FTP folder, the problem is that the filename is having spaces and the filename is having a specific pattern.
Below is the code I have implemented:
import sys
from ftplib import FTP
import os
import socket
import time
import pandas as pd
import numpy as np
from glob import glob
import datetime as dt
from __future__ import with_statement
ftp = FTP('')
ftp.login('','')
ftp.cwd('')
ftp.retrlines('LIST')
filematch='*Elig.xlsx'
downloaded = []
for filename in ftp.nlst(filematch):
fhandle=open(filename, 'wb')
print 'Getting ' + filename
ftp.retrbinary('RETR '+ filename, fhandle.write)
fhandle.close()
downloaded.append(filename)
ftp.quit()
I understand that I can append an empty list to ftp.dir() command, but since the filename is having spaces, I am unable to split it in the right way and pick the latest file of the type that I have mentined above.
Any help would be great.
You can get the file mtime by sending the MDTM command iff the FTP server supports it and sort the files on the FTP server accordingly.
def get_newest_files(ftp, limit=None):
"""Retrieves newest files from the FTP connection.
:ftp: The FTP connection to use.
:limit: Abort after yielding this amount of files.
"""
files = []
# Decorate files with mtime.
for filename in ftp.nlst():
response = ftp.sendcmd('MDTM {}'.format(filename))
_, mtime = response.split()
files.append((mtime, filename))
# Sort files by mtime and break after limit is reached.
for index, decorated_filename in enumerate(sorted(files, reverse=True)):
if limit is not None and index >= limit:
break
_, filename = decorated_filename # Undecorate
yield filename
downloaded = []
# Retrieves the newest file from the FTP server.
for filename in get_newest_files(ftp, limit=1):
print 'Getting ' + filename
with open(filename, 'wb') as file:
ftp.retrbinary('RETR '+ filename, file.write)
downloaded.append(filename)
The issue is that the FTP "LIST" command returns text for humans, which format depends on the FTP server implementation.
Using PyFilesystem (in place of the standard ftplib) and its API will provide a "list" API (search "walk") that provide Pythonic structures of the file and directories lists hosted in the FTP server.
http://pyfilesystem2.readthedocs.io/en/latest/index.html
I am working on creating a script to watch a folder, grab any new .zip files, and then upload them via FTP to a predetermined area. Right now FTP testing is being performed Locally, since the environment isnt yet created.
The strategy I am taking is to first, unzip into a local folder. Then, perform ftplib.storbinary , on the file from the local folder, to the ftpdestination. However, the unzipping process appears to be working but I am getting a "file does not exist" error, all though I can see it in the folder itself.
Also, is there anyway to unzip directly into an FTP location? I havent been able to find a way hence the approach I am taking.
Thanks, local ftp info removed from code. All paths that are relevant in this code will be changed, most likely to dynamic fashion, but for now this is a local environment
extractZip2.py
import zipfile
import ftplib
import os
import logging
import time
from socket import error as socket_error
#Logging Setup
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('__name__')
FTPaddress = ''
FTPusername = ''
FTPpassword = ''
ftp_destination_location = ''
path_to_watch = "C:/Users/206420055/Desktop/test2/"
before = dict ([(f,None) for f in os.listdir(path_to_watch)])
temp_destination_location = "C:/Users/206420055/Desktop/temp/"
def unzip(fullPath,temporaryPath):
with zipfile.ZipFile(fullPath, "r") as z :
logger.info("Unzipping {0}".format(fullPath))
z.extractall(temporaryPath)
logger.info("Unzipped into local directory {0}".format(temp_destination_location))
def check_or_create_ftp(session, folder):
"""
Checks to see if necessary folder for currenttab is available.
Creates the folder if not found, and enters it.
"""
if folder not in session.nlst():
logger.info('Directory for {0} does not exist, creating directory\n'.format(folder))
session.mkd(folder)
session.cwd(folder)
def check_or_create(temp_destination):
"""
Checks to see if local savepath exists. Will create savepath if not exists.
"""
if not os.path.exists(temp_destination):
logger.info('Directory for %s does not exist, creating directory\n' % temp_destination)
os.makedirs(str(temp_destination))
def transfer(address,username,password,filename,destination):
logger.info("Creating Session")
try:
session = session_init(address,username,password,destination)
except (socket_error,ftplib.error_perm) as e:
logger.error(str(e))
logger.error("Error in Session Init")
else:
try:
logger.info("Sending File {0}".format(filename))
send_file(session,filename)
except (IOError, OSError, ftplib.error_perm) as e:
logger.error(e)
def session_init(address,username,password,path):
session = ftplib.FTP(address,username,password)
check_or_create_ftp(session,path)
logger.info("Session Established")
return session
def send_file(session,filename):
file = open(filename,'rb')
logger.info('Sending File : STOR '+filename)
session.storbinary('STOR '+ filename, file)
file.close()
def delete_local_files(savepath, file):
logger.info("Cleaning Up Folder {0}".format(savepath))
os.remove(file)
while 1:
time.sleep(5)
after = dict ([(f,None) for f in os.listdir(path_to_watch)])
added = [f for f in after if not f in before]
removed = [f for f in before if not f in after]
if added: print "Added: ",", ".join(added)
before = after
check_or_create(temp_destination_location)
if added :
for file in added:
print file
if file.endswith('.zip'):
unzip(path_to_watch+file, temp_destination_location)
temp_files = os.listdir(temp_destination_location)
print("Temp Files {0}".format(temp_files))
for tf in temp_files:
print("TF {0}".format(tf))
transfer(FTPaddress,FTPusername,FTPpassword,tf,ftp_destination_location)
#delete_local_files(temp_destination_location,tf)
else:
pass
edit: adding error image
Seen above, we see the file in the temp folder. But the console obviously shows the error.
just change it to
from glob import glob
zips_in_path = dict ([(f,None) for f in glob("{base_path}/*.zip".format(base_path = path_to_watch)])
os.listdir does not include the path_to_watch part of the path it is just the filenames, however glob does.
so you could also do
after = dict ([(os.path.join(path_to_watch,f),None) for f in os.listdir(path_to_watch)])
using either of these methods you should be able to get the full path to the files in the path
This may be a stupid question. My code works fine and says that I've downloaded each file from the FTP server, but when I go to the folder on my computer where my code is the downloaded files are not there. It has worked in the past but all of sudden I dont know where my files are. What is the problem? Thanks for any help
#Extract UV files from FTP server
import ftplib
try:
# Connection information
server = 'ftp.ncep.noaa.gov'
username = 'anonymous'
password = 'anything'
# Directory and matching information
directory = '/pub/data/nccf/com/hourly/prod/uv.20130729/'#Format:YearMonthDay Remeber to change date
filematch = '*.grib2'
# Establish the connection
ftp = ftplib.FTP(server)
ftp.login(username, password)
# Change to the proper directory
ftp.cwd(directory)
# Loop through matching files and download each one individually
for filename in ftp.nlst(filematch):
fhandle = open(filename, 'wb')
print 'Getting ' + filename
ftp.retrbinary('RETR ' + filename, fhandle.write)
fhandle.close()
except ftplib.all_errors as err:
print 'Change date on directory'
print err
I am using Python and trying to connect to SFTP and want to retrieve an XML file from there and need to place it in my local system. Below is the code:
import paramiko
sftpURL = 'sftp.somewebsite.com'
sftpUser = 'user_name'
sftpPass = 'password'
ssh = paramiko.SSHClient()
# automatically add keys without requiring human intervention
ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy() )
ssh.connect(sftpURL, username=sftpUser, password=sftpPass)
ftp = ssh.open_sftp()
files = ftp.listdir()
print files
Here connection is success full. And now I want to see all the folders and all the files and need to enter in to required folder for retrieving the XML file from there.
Finally my intention is to view all the folders and files after connecting to SFTP server.
In the above code I had used ftp.listdir() through which I got output as some thing like below
['.bash_logout', '.bash_profile', '.bashrc', '.mozilla', 'testfile_248.xml']
I want to know whether these are the only files present?
And the command I used above is right to view the folders too?
What is the command to view all the folders and files?
The SFTPClient.listdir returns everything, files and folders.
Were there folders, to tell them from the files, use SFTPClient.listdir_attr instead. It returns a collection of SFTPAttributes.
from stat import S_ISDIR, S_ISREG
sftp = ssh.open_sftp()
for entry in sftp.listdir_attr(remotedir):
mode = entry.st_mode
if S_ISDIR(mode):
print(entry.filename + " is folder")
elif S_ISREG(mode):
print(entry.filename + " is file")
The accepted answer by #Oz123 is inefficient. SFTPClient.listdir internally calls SFTPClient.listdir_attr and throws most information away returning file and folder names only. The answer then uselessly and laboriously re-retrieves all that data by calling SFTPClient.lstat for each file.
See also How to fetch sizes of all SFTP files in a directory through Paramiko.
Obligatory warning: Do not use AutoAddPolicy – You are losing a protection against MITM attacks by doing so. For a correct solution, see Paramiko "Unknown Server"
One quick solution is to examine the output of lstat of each object in ftp.listdir().
Here is how you can list all the directories.
>>> for i in ftp.listdir():
... lstatout=str(ftp.lstat(i)).split()[0]
... if 'd' in lstatout: print i, 'is a directory'
...
Files are the opposite search:
>>> for i in ftp.listdir():
... lstatout=str(ftp.lstat(i)).split()[0]
... if 'd' not in lstatout: print i, 'is a file'
...
Here is a solution I have come up with. Based on https://stackoverflow.com/a/59109706 . My solution gives a pretty output.
Update I have modified it slightly to incorporate Martin's suggestions. Now my code is considerably fast compared to my initial version using isdir and listdir
# prefix components:
space = ' '
branch = '│ '
# pointers:
tee = '├── '
last = '└── '
def stringpath(path):
# just a helper to get string of PosixPath
return str(path)
from pathlib import Path
from stat import S_ISDIR
def tree_sftp(sftp, path='.', parent='/', prefix=''):
"""
Loop through files to print it out
for file in tree_sftp(sftp):
print(file)
"""
fullpath = Path(parent, path)
strpath = stringpath(fullpath)
dirs = sftp.listdir_attr(strpath)
pointers = [tee] * (len(dirs) - 1) + [last]
pdirs = [Path(fullpath, d.filename) for d in dirs]
sdirs = [stringpath(path) for path in pdirs]
for pointer, sd, d in zip(pointers, sdirs, dirs):
yield prefix + pointer + d.filename
if S_ISDIR(d.st_mode):
extension = branch if pointer == tee else space
yield from tree_sftp(sftp, sd, prefix=prefix + extension)
You can try it out like this using pysftp
import pysftp
with pysftp.Connection(HOSTNAME, USERNAME, PASSWORD) as sftp:
for file in tree_sftp(sftp):
print(file)
Let me know if if works for you.