局域網傳輸文件是最最常用的功能,我參考https://www.jb51.net/python/345837qrz.htm這篇文章,復制粘貼,開發了一個。但發現進度條沒有用,也沒有顯示傳輸用時和傳輸速度的功能,于是我改寫了代碼,使它實現這個功能。
import socket
import osdef start_server(host='192.168.1.145', port=8888):server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind((host, port))server_socket.listen(1)print(f"服務器啟動,監聽 {host}:{port}")while True:client_socket, addr = server_socket.accept()print(f"連接來自 {addr}")# 接收文件名和大小file_info = client_socket.recv(1024).decode()file_name, file_size = file_info.split('|')file_size = int(file_size)# 創建文件并寫入數據with open('d:/socketfile/'+file_name, 'wb') as file:received = 0while received < file_size:data = client_socket.recv(4096)file.write(data)received += len(data)print(f"接收進度: {received}/{file_size} 字節")client_socket.close()print(f"文件 {file_name} 接收完成")if __name__ == "__main__":start_server()
上面是服務器端,沒有怎么改,增加了'd:/socketfile/'+file_name,把文件放在我們想要放的文件夾。
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 8 14:31:28 2025@author: YBK
"""import socket
import os
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import threading
from tkinter import ttk
import timeclass FileTransferGUI:def __init__(self, master):self.master = mastermaster.title("局域網文件傳輸工具")master.geometry("500x400")# 輸入服務器IPtk.Label(master, text="服務器IP:").pack(pady=5)self.ip_entry = tk.Entry(master, width=30)self.ip_entry.pack(pady=5)self.ip_entry.insert(0, "192.168.1.145") # 默認IP,根據實際修改#發生使用時間、傳輸速度self.usetime = 0self.sudu = 0# 文件選擇按鈕tk.Button(master, text="選擇文件", command=self.select_file).pack(pady=10)self.file_path = ""# 發送按鈕tk.Button(master, text="發送文件", command=self.start_send).pack(pady=10)# 進度顯示self.progress_var = tk.DoubleVar()self.progress_bar = ttk.Progressbar(master, variable=self.progress_var, maximum=100)self.progress_bar.pack(fill=tk.X, padx=20, pady=10)# 日志區域self.log_area = scrolledtext.ScrolledText(master, height=10)self.log_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)self.log_area.config(state=tk.DISABLED)def send_file(self,server_ip, file_path, port=8888): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:client_socket.connect((server_ip, port))file_name = os.path.basename(file_path)file_size = os.path.getsize(file_path)# 發送文件信息client_socket.send(f"{file_name}|{file_size}".encode())# 分塊發送文件數據with open(file_path, 'rb') as file:sent = 0start = time.time()while sent < file_size:data = file.read(4096)client_socket.send(data)sent += len(data)# print(f"發送進度: {sent}/{file_size} 字節")self.progress_var.set(int(sent/file_size*100))self.usetime = time.time() - startself.sudu = file_size / 1024 / 1024 / self.usetimeprint(f"文件發送成功,耗時{self.usetime:.2f}秒,速度:{self.sudu:.2f}m/s")except Exception as e:print(f"錯誤: {e}")finally:client_socket.close()def select_file(self):self.file_path = filedialog.askopenfilename()if self.file_path:self.log(f"已選擇文件: {self.file_path}")def start_send(self):server_ip = self.ip_entry.get()if not server_ip or not self.file_path:messagebox.showerror("錯誤", "請輸入IP并選擇文件")return# 在新線程中發送文件,避免GUI凍結threading.Thread(target=self.send_file_thread, args=(server_ip, self.file_path)).start()def send_file_thread(self, server_ip, file_path):try:self.log("開始發送文件...")self.send_file(server_ip, file_path) # 調用步驟2的發送函數self.progress_var.set(100)self.log(f"文件發送完成!耗時{self.usetime:.4f}秒,速度:{self.sudu:.2f}m/s")except Exception as e:self.log(f"錯誤: {e}")def log(self, message):self.log_area.config(state=tk.NORMAL)self.log_area.insert(tk.END, message + "\n")self.log_area.config(state=tk.DISABLED)self.log_area.yview(tk.END)if __name__ == "__main__":root = tk.Tk()app = FileTransferGUI(root)root.mainloop()
以上就改動比較大,也讓我慢慢熟悉面向對象編程,用self.可以為公用變量,增加時間和速度的顯示,原來代碼出現tk.Progressbar報錯,進度條無法正常顯示等問題。
速度還是可以的。
用一段時間,感覺還是不爽,要開2個程序,一個是服務器端,一個是客戶端,才能在2臺電腦中互傳文件,為此,我把它一體化,只要用一個程序就實現了服務器端和客戶端的功能,又能接受文件,又能發送文件。同時,加上我一直喜歡用的拖放功能,不能每次去選擇文件傳輸。
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 8 14:31:28 2025@author: YBK
"""import socket
import os
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import threading
from tkinter import ttk
import time
import windnd
from tkinter import LabelFrame
import chardetdef start_server(host='192.168.1.140', port=8888):server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind((host, port))server_socket.listen(1)print(f"服務器啟動,監聽 {host}:{port}")while True:client_socket, addr = server_socket.accept()print(f"連接來自 {addr}")# 接收文件名和大小raw_data = client_socket.recv(1024)encoding = chardet.detect(raw_data)['encoding']file_info = raw_data.decode(encoding)file_name, file_size = file_info.split('|')file_size = int(file_size)# 創建文件并寫入數據with open('e:/socketfile/'+file_name, 'wb') as file:received = 0while received < file_size:data = client_socket.recv(4096)file.write(data)received += len(data)print(f"接收進度: {received}/{file_size} 字節")client_socket.close()print(f"文件 {file_name} 接收完成")class FileTransferGUI:def __init__(self, master):self.master = mastermaster.title("局域網文件傳輸工具")master.geometry("500x600")# 輸入服務器IPtk.Label(master, text="服務器IP:").pack(pady=5)self.ip_entry = tk.Entry(master, width=30)self.ip_entry.pack(pady=5)self.ip_entry.insert(0, "192.168.1.145") # 默認IP,根據實際修改tk.Label(master, text="本機IP(作為服務器):").pack(pady=5)self.bip_entry = tk.Entry(master, width=30)self.bip_entry.pack(pady=5)self.bip_entry.insert(0, "192.168.1.140") # 默認IP,根據實際修改# 啟動服務器按鈕self.bt_fwq = tk.Button(master, text="啟動服務器", command=self.start_fwq)self.bt_fwq.pack(pady=10)#發生使用時間self.usetime = 0self.sudu = 0# 文件選擇按鈕tk.Button(master, text="選擇文件", command=self.select_file).pack(pady=10)self.file_path = ""# 文件拖放區域self.f_frame = LabelFrame(master, text="也可以拖放文件到此", width=250)self.f_frame.pack(expand=True, padx=10)self.label1 = tk.Label(self.f_frame,wraplength=580,text="")self.label1.pack(anchor="w", pady=5)windnd.hook_dropfiles(self.f_frame, func=self.handle_file)# 發送按鈕tk.Button(master, text="發送文件", command=self.start_send).pack(pady=10)# 進度顯示self.progress_var = tk.DoubleVar()self.progress_bar = ttk.Progressbar(master, variable=self.progress_var, maximum=100)self.progress_bar.pack(fill=tk.X, padx=20, pady=10)# 日志區域self.log_area = scrolledtext.ScrolledText(master, height=10)self.log_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)self.log_area.config(state=tk.DISABLED)def start_server(self,host='192.168.1.140', port=8888):server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind((host, port))server_socket.listen(1)print(f"服務器啟動,監聽 {host}:{port}")while True:client_socket, addr = server_socket.accept()print(f"連接來自 {addr}")# 接收文件名和大小raw_data = client_socket.recv(1024)encoding = chardet.detect(raw_data)['encoding']file_info = raw_data.decode(encoding)file_name, file_size = file_info.split('|')file_size = int(file_size)# 創建文件并寫入數據with open('e:/socketfile/'+file_name, 'wb') as file:received = 0while received < file_size:data = client_socket.recv(4096)file.write(data)received += len(data)print(f"接收進度: {received}/{file_size} 字節")client_socket.close()print(f"文件 {file_name} 接收完成")self.log(f"文件 {file_name} 接收完成")def handle_file(self,files):if len(files) > 0:path = files[0].decode('gbk').replace('\\', '/')self.file_path = pathif self.file_path:self.log(f"已選擇文件: {self.file_path}")def start_fwq(self):fwq_ip = self.bip_entry.get()if not fwq_ip:messagebox.showerror("錯誤", "請輸入IP并選擇文件")return# print(fwq_ip)# 在新線程中發送文件,避免GUI凍結threading.Thread(target=self.start_server, args=(fwq_ip, 8888)).start()self.bt_fwq.config(state="disabled", bg="lightgray")def send_file(self,server_ip, file_path, port=8888): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:client_socket.connect((server_ip, port))file_name = os.path.basename(file_path)file_size = os.path.getsize(file_path)# 發送文件信息client_socket.send(f"{file_name}|{file_size}".encode())# 分塊發送文件數據with open(file_path, 'rb') as file:sent = 0start = time.time()while sent < file_size:data = file.read(4096)client_socket.send(data)sent += len(data)# print(f"發送進度: {sent}/{file_size} 字節")self.progress_var.set(int(sent/file_size*100))self.usetime = time.time() - startself.sudu = file_size / 1024 / 1024 / self.usetimeprint(f"文件發送成功,耗時{self.usetime:.2f}秒,速度:{self.sudu:.2f}m/s")except Exception as e:print(f"錯誤: {e}")finally:client_socket.close()def select_file(self):self.file_path = filedialog.askopenfilename()if self.file_path:self.log(f"已選擇文件: {self.file_path}")def start_send(self):server_ip = self.ip_entry.get()if not server_ip or not self.file_path:messagebox.showerror("錯誤", "請輸入IP并選擇文件")return# 在新線程中發送文件,避免GUI凍結threading.Thread(target=self.send_file_thread, args=(server_ip, self.file_path)).start()def send_file_thread(self, server_ip, file_path):try:self.log("開始發送文件...")self.send_file(server_ip, file_path) # 調用步驟2的發送函數self.progress_var.set(100)self.log(f"文件發送完成!耗時{self.usetime:.4f}秒,速度:{self.sudu:.2f}m/s")except Exception as e:self.log(f"錯誤: {e}")def log(self, message):self.log_area.config(state=tk.NORMAL)self.log_area.insert(tk.END, message + "\n")self.log_area.config(state=tk.DISABLED)self.log_area.yview(tk.END)if __name__ == "__main__":root = tk.Tk()app = FileTransferGUI(root)root.mainloop()
填好ip,點擊啟動服務器就可以接收文件。下面還能顯示接收到的文件。