Python如何实现FTP功能
Python版本
实现了比之前的xxftp更多更完善的功能
1、继续支持多用户
2、继续支持虚拟目录
3、增加支持用户根目录以及映射虚拟目录的权限设置
4、增加支持限制用户根目录或者虚拟目录的空间大小
xxftp的特点
1、开源、跨平台
2、简单、易用
3、不需要数据库
4、可扩展性超强
5、你可以免费使用xxftp假设自己的私人FTP服务器
匿名帐号可以使用!
匿名根目录只读,映射了一个虚拟目录,可以上传文件但不允许更改!
使用方法
跟用C语言写的xxftp使用方法一样
FTP服务器目录结构
-/root -xxftp.welcome -xxftp.goodbye -user1 -.xxftp -password -... -user2 -.xxftp -password -... -anonymous源代码
代码如下:
importsocket,threading,os,sys,time importhashlib,platform,stat listen_ip="localhost" listen_port=21 conn_list=[] root_dir="./home" max_connections=500 conn_timeout=120 classFtpConnection(threading.Thread): def__init__(self,fd): threading.Thread.__init__(self) self.fd=fd self.running=True self.setDaemon(True) self.alive_time=time.time() self.option_utf8=False self.identified=False self.option_pasv=True self.username="" defprocess(self,cmd,arg): cmd=cmd.upper(); ifself.option_utf8: arg=unicode(arg,"utf8").encode(sys.getfilesystemencoding()) print"<<",cmd,arg,self.fd #FtpCommand ifcmd=="BYE"orcmd=="QUIT": ifos.path.exists(root_dir+"/xxftp.goodbye"): self.message(221,open(root_dir+"/xxftp.goodbye").read()) else: self.message(221,"Bye!") self.running=False return elifcmd=="USER": #SetAnonymousUser ifarg=="":arg="anonymous" forcinarg: ifnotc.isalpha()andnotc.isdigit()andc!="_": self.message(530,"Incorrectusername.") return self.username=arg self.home_dir=root_dir+"/"+self.username self.curr_dir="/" self.curr_dir,self.full_path,permission,self.vdir_list,\ limit_size,is_virtual=self.parse_path("/") ifnotos.path.isdir(self.home_dir): self.message(530,"User"+self.username+"notexists.") return self.pass_path=self.home_dir+"/.xxftp/password" ifos.path.isfile(self.pass_path): self.message(331,"Passwordrequiredfor"+self.username) else: self.message(230,"Identified!") self.identified=True return elifcmd=="PASS": ifopen(self.pass_path).read()==hashlib.md5(arg).hexdigest(): self.message(230,"Identified!") self.identified=True else: self.message(530,"Notidentified!") self.identified=False return elifnotself.identified: self.message(530,"PleaseloginwithUSERandPASS.") return self.alive_time=time.time() finish=True ifcmd=="NOOP": self.message(200,"ok") elifcmd=="TYPE": self.message(200,"ok") elifcmd=="SYST": self.message(200,"UNIX") elifcmd=="EPSV"orcmd=="PASV": self.option_pasv=True try: self.data_fd=socket.socket(socket.AF_INET,socket.SOCK_STREAM) self.data_fd.bind((listen_ip,0)) self.data_fd.listen(1) ip,port=self.data_fd.getsockname() ifcmd=="EPSV": self.message(229,"EnteringExtendedPassiveMode(|||"+str(port)+"|)") else: ipnum=socket.inet_aton(ip) self.message(227,"EnteringPassiveMode(%s,%u,%u)."% (",".join(ip.split(".")),(port>>8&0xff),(port&0xff))) except: self.message(500,"failedtocreatedatasocket.") elifcmd=="EPRT": self.message(500,"implementEPRTlater...") elifcmd=="PORT": self.option_pasv=False self.data_fd=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s=arg.split(",") self.data_ip=".".join(s[:4]) self.data_port=int(s[4])*256+int(s[5]) self.message(200,"ok") elifcmd=="PWD"orcmd=="XPWD": ifself.curr_dir=="":self.curr_dir="/" self.message(257,'"'+self.curr_dir+'"') elifcmd=="LIST"orcmd=="NLST": ifarg!=""andarg[0]=="-":arg=""#omitparameters remote,local,perm,vdir_list,limit_size,is_virtual=self.parse_path(arg) ifnotos.path.exists(local): self.message(550,"failed.") return ifnotself.establish():return self.message(150,"ok") forvinvdir_list: f=v[0] ifself.option_utf8: f=unicode(f,sys.getfilesystemencoding()).encode("utf8") ifcmd=="NLST": info=f+"\r\n" else: info="d%s%s-------%04u%8s%8s%8lu%s%s\r\n"%( "r"if"read"inpermelse"-", "w"if"write"inpermelse"-", 1,"0","0",0, time.strftime("%b%d%Y",time.localtime(time.time())), f) self.data_fd.send(info) forfinos.listdir(local): iff[0]==".":continue path=local+"/"+f ifself.option_utf8: f=unicode(f,sys.getfilesystemencoding()).encode("utf8") ifcmd=="NLST": info=f+"\r\n" else: st=os.stat(path) info="%s%s%s-------%04u%8s%8s%8lu%s%s\r\n"%( "-"ifos.path.isfile(path)else"d", "r"if"read"inpermelse"-", "w"if"write"inpermelse"-", 1,"0","0",st[stat.ST_SIZE], time.strftime("%b%d%Y",time.localtime(st[stat.ST_MTIME])), f) self.data_fd.send(info) self.message(226,"Limitsize:"+str(limit_size)) self.data_fd.close() self.data_fd=0 elifcmd=="REST": self.file_pos=int(arg) self.message(250,"ok") elifcmd=="FEAT": features="211-Features:\r\nSITES\r\nEPRT\r\nEPSV\r\nMDTM\r\nPASV\r\n"\ "RESTSTREAM\r\nSIZE\r\nUTF8\r\n211End\r\n" self.fd.send(features) elifcmd=="OPTS": arg=arg.upper() ifarg=="UTF8ON": self.option_utf8=True self.message(200,"ok") elifarg=="UTF8OFF": self.option_utf8=False self.message(200,"ok") else: self.message(500,"unrecognizedoption") elifcmd=="CDUP": finish=False arg=".." else: finish=False iffinish:return #Parseargument(It'sapath) ifarg=="": self.message(500,"where'smyargument?") return remote,local,permission,vdir_list,limit_size,is_virtual=\ self.parse_path(arg) #cannotdoanythingtovirtualdirectory ifis_virtual:permission="none" can_read,can_write,can_modify="read"inpermission,"write"inpermission,"modify"inpermission newpath=local try: ifcmd=="CWD": if(os.path.isdir(newpath)): self.curr_dir=remote self.full_path=newpath self.message(250,'"'+remote+'"') else: self.message(550,"failed") elifcmd=="MDTM": ifos.path.exists(newpath): self.message(213,time.strftime("%Y%m%d%I%M%S",time.localtime( os.path.getmtime(newpath)))) else: self.message(550,"failed") elifcmd=="SIZE": self.message(231,os.path.getsize(newpath)) elifcmd=="XMKD"orcmd=="MKD": ifnotcan_modify: self.message(550,"permissiondenied.") return os.mkdir(newpath) self.message(250,"ok") elifcmd=="RNFR": ifnotcan_modify: self.message(550,"permissiondenied.") return self.temp_path=newpath self.message(350,"renamefrom"+remote) elifcmd=="RNTO": os.rename(self.temp_path,newpath) self.message(250,"RNTOto"+remote) elifcmd=="XRMD"orcmd=="RMD": ifnotcan_modify: self.message(550,"permissiondenied.") return os.rmdir(newpath) self.message(250,"ok") elifcmd=="DELE": ifnotcan_modify: self.message(550,"permissiondenied.") return os.remove(newpath) self.message(250,"ok") elifcmd=="RETR": ifnotos.path.isfile(newpath): self.message(550,"failed") return ifnotcan_read: self.message(550,"permissiondenied.") return ifnotself.establish():return self.message(150,"ok") f=open(newpath,"rb") whileself.running: self.alive_time=time.time() data=f.read(8192) iflen(data)==0:break self.data_fd.send(data) f.close() self.data_fd.close() self.data_fd=0 self.message(226,"ok") elifcmd=="STOR"orcmd=="APPE": ifnotcan_write: self.message(550,"permissiondenied.") return ifos.path.exists(newpath)andnotcan_modify: self.message(550,"permissiondenied.") return #Checkspacesizeremained! used_size=0 iflimit_size>0: used_size=self.get_dir_size(os.path.dirname(newpath)) ifnotself.establish():return self.message(150,"ok") f=open(newpath,("ab"ifcmd=="APPE"else"wb")) whileself.running: self.alive_time=time.time() data=self.data_fd.recv(8192) iflen(data)==0:break iflimit_size>0: used_size=used_size+len(data) ifused_size>limit_size:break f.write(data) f.close() self.data_fd.close() self.data_fd=0 iflimit_size>0andused_size>limit_size: self.message(550,"Exceedinguserspacelimit:"+str(limit_size)+"bytes") else: self.message(226,"ok") else: self.message(500,cmd+"notimplemented") except: self.message(550,"failed.") defestablish(self): ifself.data_fd==0: self.message(500,"nodataconnection") returnFalse ifself.option_pasv: fd=self.data_fd.accept()[0] self.data_fd.close() self.data_fd=fd else: try: self.data_fd.connect((self.data_ip,self.data_port)) except: self.message(500,"failedtoestablishdataconnection") returnFalse returnTrue defread_virtual(self,path): vdir_list=[] path=path+"/.xxftp/virtual" ifos.path.isfile(path): forvinopen(path,"r").readlines(): items=v.split() items[1]=items[1].replace("$root",root_dir) vdir_list.append(items) returnvdir_list defget_dir_size(self,folder): size=0 forpath,dirs,filesinos.walk(folder): forfinfiles: size+=os.path.getsize(os.path.join(path,f)) returnsize defread_size(self,path): size=0 path=path+"/.xxftp/size" ifos.path.isfile(path): size=int(open(path,"r").readline()) returnsize defread_permission(self,path): permission="read,write,modify" path=path+"/.xxftp/permission" ifos.path.isfile(path): permission=open(path,"r").readline() returnpermission defparse_path(self,path): ifpath=="":path="." ifpath[0]!="/": path=self.curr_dir+"/"+path s=os.path.normpath(path).replace("\\","/").split("/") local=self.home_dir #resetdirectorypermission vdir_list=self.read_virtual(local) limit_size=self.read_size(local) permission=self.read_permission(local) remote="" is_virtual=False fornameins: name=name.lstrip(".") ifname=="":continue remote=remote+"/"+name is_virtual=False forvinvdir_list: ifv[0]==name: permission=v[2] local=v[1] limit_size=self.read_size(local) is_virtual=True ifnotis_virtual:local=local+"/"+name vdir_list=self.read_virtual(local) return(remote,local,permission,vdir_list,limit_size,is_virtual) defrun(self): '''ConnectionProcess''' try: iflen(conn_list)>max_connections: self.message(500,"toomanyconnections!") self.fd.close() self.running=False return #WelcomeMessage ifos.path.exists(root_dir+"/xxftp.welcome"): self.message(220,open(root_dir+"/xxftp.welcome").read()) else: self.message(220,"xxftp(Python)www.xiaoxia.org") #CommandLoop line="" whileself.running: data=self.fd.recv(4096) iflen(data)==0:break line+=data ifline[-2:]!="\r\n":continue line=line[:-2] space=line.find("") ifspace==-1: self.process(line,"") else: self.process(line[:space],line[space+1:]) line="" except: print"error",sys.exc_info() self.running=False self.fd.close() print"connectionend",self.fd,"user",self.username defmessage(self,code,s): '''SendFtpMessage''' s=str(s).replace("\r","") ss=s.split("\n") iflen(ss)>1: r=(str(code)+"-")+("\r\n"+str(code)+"-").join(ss[:-1]) r+="\r\n"+str(code)+""+ss[-1]+"\r\n" else: r=str(code)+""+ss[0]+"\r\n" ifself.option_utf8: r=unicode(r,sys.getfilesystemencoding()).encode("utf8") self.fd.send(r) defserver_listen(): globalconn_list listen_fd=socket.socket(socket.AF_INET,socket.SOCK_STREAM) listen_fd.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) listen_fd.bind((listen_ip,listen_port)) listen_fd.listen(1024) conn_lock=threading.Lock() print"ftpdislisteningon",listen_ip+":"+str(listen_port) whileTrue: conn_fd,remote_addr=listen_fd.accept() print"connectionfrom",remote_addr,"conn_list",len(conn_list) conn=FtpConnection(conn_fd) conn.start() conn_lock.acquire() conn_list.append(conn) #checktimeout try: curr_time=time.time() forconninconn_list: ifint(curr_time-conn.alive_time)>conn_timeout: ifconn.running==True: conn.fd.shutdown(socket.SHUT_RDWR) conn.running=False conn_list=[connforconninconn_listifconn.running] except: printsys.exc_info() conn_lock.release() defmain(): server_listen() if__name__=="__main__": main()
内容扩展:
FTP服务器端代码:
importsocket,os,time importhashlib server=socket.socket() server.bind(('0.0.0.0',6666)) server.listen() print("等待....") whileTrue: conn,addr=server.accept() print("newconn:",conn) whileTrue: data=conn.recv(1024) ifnotdata: print("clientisdisconnection") break cmd,filename=data.decode().split()#记录指令和文件名 print(filename) #判断当前目录是否存在该文件,而且必须是文件,而不是目录 ifos.path.isfile(filename): f=open(filename,'rb') #m=hashlib.md5()#创建md5 file_size=os.stat(filename).st_size#stat()可以返回文件的大小值 conn.send((str(file_size)).encode())#发送文件大小 conn.recv(1024)#等待返回信息 forlineinf: #m.updata(line) conn.send(line) #print("filemd5",m.hexdigest())#打印md5值 f.close()
到此这篇关于Python如何实现FTP功能的文章就介绍到这了,更多相关Python实现的简易FTP内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!