简单的实现文件上传下载功能,其中用到了socketserver,可以实现多用户上传和下载文件
服务端代码:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Xiaobai Lei import socket import subprocess import os import json import hashlib import struct import socketserver class MyServer(socketserver.BaseRequestHandler): """文件上传下载服务端""" address_family = socket.AF_INET socket_type = socket.SOCK_STREAM # tcp流类型 allow_reuse_address = False max_packet_size = 8192 coding = 'utf-8' request_queue_size = 5 # 队列最长数量 server_upload_dir = 'file_upload' server_download_dir = 'file_download' def server_close(self): self.request.close() def close_request(self, request): request.close() def handle(self): print('from client ', self.client_address) while True: try: head_struct = self.request.recv(4) if not head_struct:break head_len = struct.unpack('i', head_struct)[0] head_json = self.request.recv(head_len).decode(self.coding) head_dic = json.loads(head_json) action = head_dic['action'] if hasattr(self, action): func = getattr(self, action) func(head_dic) except Exception: break def put(self, args): """实现文件上传""" file_path = os.path.normpath(os.path.join(self.server_upload_dir, args['filename'])) filesize = args['filesize'] recv_size = 0 # 记录读取文件的大小 file_md5 = hashlib.md5() # 计算文件的md5值,保证上传的完整性 with open(file_path, "wb") as f: while recv_size < filesize: recv_data = self.request.recv(self.max_packet_size) file_md5.update(recv_data) f.write(recv_data) recv_size += len(recv_data) file_md5_code = file_md5.hexdigest() # 获取文件md5的值,字符串形式 self.request.send(b"ok") recv_md5_code = self.request.recv(self.max_packet_size).decode(self.coding) # 接收客户端发来的md5值 if recv_md5_code == file_md5_code: print("%s文件上传成功,大小为%s字节!"%(args['filename'], filesize)) self.request.send(b"1000") else: self.request.send(b"1001") def download(self, args): """实现文件下载""" file_path = os.path.normpath(os.path.join(self.server_download_dir, args['filename'])) if not os.path.exists(file_path): print('file:%s is not exists' % file_path) return else: filesize = os.path.getsize(file_path) filesize_struct = struct.pack('i', filesize) # 发送服务端文件的大小 self.request.send(filesize_struct) send_size = 0 # 记录读取文件的大小 file_md5 = hashlib.md5() # 计算文件的md5值,保证上传的完整性 # 接下来传输文件 with open(file_path, "rb") as f: for line in f: file_md5.update(line) self.request.send(line) send_size += len(line) # 发送文件的md5,判断文件一致性 file_md5_code = file_md5.hexdigest() # 获取文件md5的值,字符串形式 file_md5_client = self.request.recv(self.max_packet_size).decode(self.coding) if file_md5_client == file_md5_code: print("%s文件下载成功,大小为%s字节!" % (args['filename'], filesize)) self.request.send(b"1000") else: self.request.send(b"1001") if __name__ == '__main__': ip_port = ('127.0.0.1', 9999) server = socketserver.ThreadingTCPServer(ip_port, MyServer) server.serve_forever()
客户端代码:
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Xiaobai Lei import socket import subprocess import os import json import hashlib import struct class MyTcpClient: address_family = socket.AF_INET socket_type = socket.SOCK_STREAM allow_reuse_address = False max_packet_size = 8192 coding = 'utf-8' request_queue_size = 5 # client_download_dir = 'file_download' def __init__(self, server_address, connect=True): self.server_address = server_address self.socket = socket.socket(self.address_family, self.socket_type) if connect: try: self.client_connect() except: self.client_close() raise def client_connect(self): self.socket.connect(self.server_address) def client_close(self): self.socket.close() def run(self): while True: cmd = input(">>: ").strip() if not cmd:continue action,filename = cmd.split() if hasattr(self, action): func = getattr(self, action) func((action,filename)) def put(self, args): """实现文件上传""" action, filename = args[0],args[1] if not os.path.isfile(filename): print('file:%s is not exists' % filename) return else: filesize = os.path.getsize(filename) head_dic = {'action': action, 'filename': os.path.basename(filename), 'filesize': filesize} head_json = json.dumps(head_dic) head_json_bytes = head_json.encode('utf8') head_struct = struct.pack("i", len(head_json_bytes)) # 先发送字典数据的pack字节 self.socket.send(head_struct) # 接着发送字典数据 self.socket.send(head_json_bytes) send_size = 0 file_md5 = hashlib.md5() # 最后发送需要传输的文件 with open(filename, 'rb') as f: for line in f: file_md5.update(line) self.socket.send(line) send_size += len(line) # 发送文件的md5,判断文件一致性 file_md5_code = file_md5.hexdigest() # 获取文件md5的值,字符串形式 self.socket.recv(self.max_packet_size) self.socket.send(file_md5_code.encode(self.coding)) # 发送文件的md5值给服务端 code = self.socket.recv(self.max_packet_size).decode(self.coding) if code == "1000": print("%s字节的%s文件上传成功!"%(send_size, filename)) else: print("上传失败,请重新上传!") def download(self, args): """实现文件下载""" print(args) action, filename = args[0], args[1] head_dic = {'action': action, 'filename': os.path.basename(filename)} head_json = json.dumps(head_dic) head_json_bytes = head_json.encode('utf8') head_struct = struct.pack("i", len(head_json_bytes)) # 先发送字典数据的pack字节 self.socket.send(head_struct) # 接着发送字典数据 self.socket.send(head_json_bytes) # 接收服务端要发送过来的文件大小数据 filesize_struct = self.socket.recv(4) filesize = struct.unpack('i', filesize_struct)[0] recv_size = 0 # 记录读取文件的大小 file_md5 = hashlib.md5() # 计算文件的md5值,保证上传的完整性 with open(filename, "wb") as f: while recv_size < filesize: recv_data = self.socket.recv(self.max_packet_size) file_md5.update(recv_data) f.write(recv_data) recv_size += len(recv_data) file_md5_code = file_md5.hexdigest() # 获取文件md5的值,字符串形式 self.socket.send(file_md5_code.encode(self.coding)) # 发送文件的md5值给服务端 code = self.socket.recv(self.max_packet_size).decode(self.coding) if code == "1000": print("%s字节的%s文件下载成功!"%(recv_size, filename)) else: print("下载失败,请重新下载!") if __name__ == '__main__': client = MyTcpClient(('127.0.0.1', 9999)) client.run()