def download(self, filename): source = self.current_path + filename # 远程服务器文件路径 target = self.root_path + filename # 下载到本地的路径 source_filelist = self.list() # 获取远程服务器当前目录的文件,判断是否有目标文件 if not filename in source_filelist: # 如果不存在就退出 print("Target file does not exist") return if self.lockfile(self.current_path, filename) != 0: # 对文件申请加锁 print("The file is being edited by someone else") return response = self.stub.download( FileServer_pb2.Download_Request(download_path=source)) if os.path.exists(target): # # 如果目标文件已经存在,就在名字后面加个 - copy n n表示第几个副本 tmp1 = os.listdir(self.root_path) # 获取当前所有文件名列表 n = 1 for a in tmp1: # 如果这个文件名字被当前文件包含,n就+1, 注意:文件名只有后缀才允许包含点 if filename.split('.')[-2] in a and 'rename' in a: n += 1 ### 根据副本号n 生成新的目标文件名 tmp2 = filename.split('.') tmp2[-2] += " - rename%s" % (n) target = self.root_path + '.'.join(tmp2) self.save_chunks_to_file(response, target) print("File downloaded successfully") self.unlockfile(self.current_path, filename) # 将文件解锁
def open(self, filename): ''' 允许用户打开并编辑一个文件,因为没有像vim一样的编辑器,所以只能模拟一个打开编辑的过程,逻辑如下: open相当于用户对一个文件进行download,并对文件加锁。 但这个锁不会再download后释放,而是一直保存直到用户主动输入close指令。 这个过程就模拟了用户打开并开始编辑文件的过程,不过打开关闭都需要用户输入指令完成 客户端会维护一个open的列表保存用户打开的所有进程用户可以查看当前所有被打开的进程 ''' target_filelist = self.list() # 获取远程服务器当前目录的文件,判断是否有目标文件 if not filename in target_filelist: # 如果不存在就退出 print("Target file does not exist") return source = self.current_path + filename # 远程服务器文件路径 target = self.root_path + filename # 下载到本地的路径 source_filelist = self.list() # 获取远程服务器当前目录的文件,判断是否有目标文件 if not filename in source_filelist: # 如果不存在就退出 print("Target file does not exist") return if self.lockfile(self.current_path, filename) != 0: # 对文件申请加锁 print("The file is being edited by someone else") return response = self.stub.download( FileServer_pb2.Download_Request(download_path=source)) if os.path.exists(target): print("Warning: Local file with the same name is overwritten!") self.save_chunks_to_file(response, target) print("File open successfully") # 打开文件成功后将文件加入到已打开文件队列中去 self.openfile_list.append((self.current_path, filename))
def synchronize_delete(self, target_path): # 服务器将文件被删除的操作同步到所有副本上 # 获取所有副本服务器地址 while True: try: response = self.dirstub.getfileserver( DS_pb2.Dir_Empty(empty=0)) break except: print("Directory server connection timed out!") for server in response.server_list: if server.Server_ID == self.id: continue # 对于其他服务器全部发送一次删除操作的指令 # 建立和其他服务器的关联 tmpchannel = grpc.insecure_channel(server.ip + ':' + server.port) tmpstub = FS_pb2_grpc.FileServerStub(tmpchannel) response = tmpstub.delete_without_syn( FS_pb2.Delete_Request(delete_path=target_path)) if response.success == 0: print("File deleted successfully") else: print("File deleted failed")
def get_file_chunks(self, source, target): with open(source, 'rb') as f: while True: piece = f.read(self.CHUNK_SIZE) if len(piece) == 0: return yield FileServer_pb2.Upload_Request( buffer=piece, target_path=target) # 同时传文件和路径
def upload_without_syn(self, request, context): # 用在同步中,避免重复同步 代码和update类似,但去掉同步的操作 try: self.save_chunks_to_server(request) success = 0 except Exception as err: print(err) success = 1 return FS_pb2.Reply(success=success)
def delete_without_syn(self, request, context): # 用在同步中,避免重复同步 代码和delete类似,但去掉同步的操作 try: self.delete_file(request.delete_path) success = 0 except Exception as err: print(err) success = 1 return FS_pb2.Reply(success=success)
def choose_server(self): # 连接到文件锁服务器: print("Connecting to the file lock server...") self.connect_lockserver() # 连接目录服务器 print("Connecting to the directory server...") self.connect_dirserver() # 从目录服务器中获取所有FS的信息 while True: try: response = self.dirstub.getfileserver( DS_pb2.Dir_Empty(empty=0)) break except: print("Directory server connection timed out!") print("Select one of the following servers:") server_dic = {} title = '{: <15}{: <15}{: <15}{: <15}'.format('ServerID', 'IP', 'port', 'location') print(title) for server in response.server_list: server_dic[server.Server_ID] = { 'ip': server.ip, 'port': server.port, 'location': server.location } info = '{: <15}{: <15}{: <15}{: <15}'.format( str(server.Server_ID), server.ip, server.port, server.location) print(info) while True: pick = int(input("Input ServerID to choose: ")) if pick not in server_dic.keys(): print("Server does not exist, please select another server!") continue print("Connecting to file server...") try: # 记录连接的文件服务器地址 self.FS_ip = server_dic[pick]['ip'] self.FS_port = server_dic[pick]['port'] channel = grpc.insecure_channel(self.FS_ip + ":" + self.FS_port) self.stub = FileServer_pb2_grpc.FileServerStub(channel) # 获取服务器的根目录地址,同时也可以检测服务器是否在线 response = self.stub.pwd(FileServer_pb2.Empty(empty=0)) self.current_path = response.pwd break except Exception as e: print( "Server connection timed out, please select another server!" ) continue
def get_file(self, path): ''' 根据文件名和当前路径获取文件并返回生成器 用在客户下载申请后的返回 ''' with open(self.root_path + path, 'rb') as f: while True: piece = f.read(self.CHUNK_SIZE) if len(piece) == 0: return yield FS_pb2.Chunk(buffer=piece)
def mkdir(self, request, context): ''' 提供用户建立目录的接口 ''' try: self.make_directory(request.dir_path) success = 0 except Exception as err: print(err) success = 1 return FS_pb2.Reply(success=success)
def delete(self, request, context): ''' 提供删除接口供用户删除文件 ''' try: self.delete_file(request.delete_path) self.synchronize_delete(request.delete_path) success = 0 except Exception as err: print(err) success = 1 return FS_pb2.Reply(success=success)
def delete(self, filename): ### 用户发消息删除文件服务器上的对应文件名 target_filelist = self.list() # 获取远程服务器当前目录的文件,判断是否有目标文件 if not filename in target_filelist: # 如果不存在就退出 print("Target file does not exist") return response = self.stub.delete( FileServer_pb2.Delete_Request(delete_path=self.current_path + filename)) if response.success == 0: print("File deleted successfully") else: print("File deleted failed")
def get_file_chunks(self, source, target): ''' 用在客户上传后,服务器要把这个操作传播到每个服务器上 注意封装成请求的时候需要封装成对方的文件地址 source为服务器绝对地址 target为服务器相对地址 ''' with open(source, 'rb') as f: while True: piece = f.read(self.CHUNK_SIZE) if len(piece) == 0: return yield FS_pb2.Upload_Request(buffer=piece, target_path=target) # 同时传文件和路径
def mkdir(self, dirname): ''' 创建文件夹 传dirname然后进行创建 ''' target_filelist = self.list() # 获取远程服务器当前目录的文件,判断是否有目标文件 if dirname in target_filelist: # 如果存在就退出 print("Target directory existed") return response = self.stub.mkdir( FileServer_pb2.Mkdir_Request(dir_path=self.current_path + dirname)) if response.success == 0: print("Folder make successfully") else: print("Folder make failed")
def upload(self, request, context): ''' proto服务 提供下载接口供用户上传文件使用 上传完后需要把文件同步到所有的文件服务器上 ''' try: target_path = self.save_chunks_to_server(request) self.synchronize_upload(target_path) success = 0 except Exception as err: print(err) success = 1 return FS_pb2.Reply(success=success)
def list(self): ## 列出当前路径下的所有文件名 类似linux 的ls语句 response = self.stub.list( FileServer_pb2.List_Request(cur_path=self.current_path)) return response.list
def pwd(self, request, context): return FS_pb2.PWD_Reply(pwd='user/') # 传一个空串 客户端只知道服务器的相对路径
def list(self, request, context): all_files = ' '.join( os.listdir(self.root_path + request.cur_path)) # 输出根path下的所有文件名到一个列表中 return FS_pb2.List_Reply(list=all_files)