Простой загрузчик файлов на файловый сервер
Собрать данный проект можно скриптом build.sh
Python:
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import requests
import threading
import re
import time
import os
PASSWORD = "Пароль"
UPLOAD_URL = "https://ссылка/upload"
class ModernUploadApp:
def __init__(self, root):
self.root = root
self.root.title("Uploader")
self.root.geometry("375x675")
self.root.resizable(False, False)
self.root.configure(bg="#1e1e1e")
self.file_paths = []
self.uploading = False
style = ttk.Style()
style.theme_use("clam")
style.configure("TFrame", background="#1e1e1e")
style.configure("TLabel", background="#1e1e1e", foreground="#cccccc", font=("Segoe UI", 9))
style.configure("TButton", font=("Segoe UI", 9, "bold"), padding=4)
style.configure("Horizontal.TProgressbar", thickness=6, background="#007acc", troughcolor="#2d2d2d")
style.configure("TCombobox", fieldbackground="#2d2d2d", background="#2d2d2d", foreground="#ffffff")
style.map("TCombobox", fieldbackground=[("readonly", "#2d2d2d")], foreground=[("readonly", "#ffffff")])
try:
self.root.iconbitmap("icon.ico")
except:
pass
self.setup_ui()
def setup_ui(self):
container = ttk.Frame(self.root)
container.pack(fill=tk.BOTH, expand=True, padx=15, pady=20)
self.title_label = tk.Label(container, text="Загрузка", font=("Segoe UI", 16, "bold"), bg="#1e1e1e", fg="#00bfff")
self.title_label.pack(pady=(10, 15))
tk.Label(container, text="Файлы:", bg="#1e1e1e", fg="#cccccc", font=("Segoe UI", 9)).pack(anchor=tk.W, pady=(0, 5))
self.listbox = tk.Listbox(container, height=12, bg="#2d2d2d", fg="#ffffff", font=("Consolas", 9),
selectbackground="#007acc", borderwidth=0, highlightthickness=0)
self.listbox.pack(fill=tk.X, pady=5)
list_btn_frame = tk.Frame(container, bg="#1e1e1e")
list_btn_frame.pack(fill=tk.X, pady=5)
tk.Button(list_btn_frame, text="Добавить", command=self.add_files,
bg="#007acc", fg="white", font=("Segoe UI", 8), border=0, padx=5, pady=3,
cursor="hand2").pack(side=tk.LEFT, padx=2)
tk.Button(list_btn_frame, text="Удалить", command=self.remove_selected,
bg="#d73a49", fg="white", font=("Segoe UI", 8), border=0, padx=5, pady=3,
cursor="hand2").pack(side=tk.LEFT, padx=2)
tk.Button(list_btn_frame, text="Очистить все", command=self.clear_all,
bg="#666666", fg="white", font=("Segoe UI", 8), border=0, padx=5, pady=3,
cursor="hand2").pack(side=tk.LEFT, padx=2)
rate_frame = tk.Frame(container, bg="#1e1e1e")
rate_frame.pack(fill=tk.X, pady=10)
tk.Label(rate_frame, text="Скорость:", bg="#1e1e1e", fg="#cccccc", font=("Segoe UI", 9)).pack(anchor=tk.W)
self.unit_var = tk.StringVar(value="КБ/с")
self.speed_var = tk.StringVar(value="25")
speed_unit_frame = tk.Frame(rate_frame, bg="#1e1e1e")
speed_unit_frame.pack(anchor=tk.W, pady=2)
speed_values = [str(i) for i in range(5, 10001, 5)]
self.speed_combo = ttk.Combobox(speed_unit_frame, textvariable=self.speed_var, values=speed_values,
state="readonly", font=("Segoe UI", 9), width=8)
self.speed_combo.pack(side=tk.LEFT)
self.unit_combo = ttk.Combobox(speed_unit_frame, textvariable=self.unit_var, values=["КБ/с", "МБ/с"],
state="readonly", font=("Segoe UI", 9), width=6)
self.unit_combo.pack(side=tk.LEFT, padx=5)
progress_frame = tk.Frame(container, bg="#1e1e1e")
progress_frame.pack(fill=tk.X, pady=15)
tk.Label(progress_frame, text="Прогресс:", bg="#1e1e1e", fg="#cccccc", font=("Segoe UI", 9)).pack(anchor=tk.W)
self.progress = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, length=330, mode='determinate',
style="Horizontal.TProgressbar")
self.progress.pack(pady=4)
self.progress_text = tk.Label(progress_frame, text="0%", bg="#1e1e1e", fg="#00bfff", font=("Segoe UI", 8))
self.progress_text.pack()
self.current_file_label = tk.Label(progress_frame, text="", bg="#1e1e1e", fg="#dddddd", font=("Segoe UI", 9, "italic"))
self.current_file_label.pack(pady=2)
self.button_frame = tk.Frame(container, bg="#1e1e1e")
self.button_frame.pack(pady=20)
self.upload_btn = tk.Button(self.button_frame, text="ЗАГРУЗИТЬ", command=self.start_upload,
bg="#28a745", fg="white", font=("Segoe UI", 11, "bold"), width=20, height=2,
border=0, cursor="hand2", activebackground="#208a38")
self.upload_btn.pack()
self.cancel_btn = tk.Button(self.button_frame, text="ОТМЕНА", command=self.cancel_upload,
bg="#d73a49", fg="white", font=("Segoe UI", 11, "bold"), width=20, height=2,
border=0, cursor="hand2", activebackground="#b12a3a")
self.status_bar = tk.Label(self.root, text="", bd=1, relief=tk.SUNKEN, anchor=tk.W, bg="#2d2d2d", fg="#aaaaaa", font=("Segoe UI", 8))
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def add_files(self):
paths = filedialog.askopenfilenames(title="Выберите файлы")
if paths:
for path in paths:
if path not in self.file_paths:
self.file_paths.append(path)
self.listbox.insert(tk.END, os.path.basename(path))
self.update_title()
def remove_selected(self):
selected_idx = self.listbox.curselection()
if selected_idx:
idx = selected_idx[0]
self.listbox.delete(idx)
del self.file_paths[idx]
self.update_title()
def clear_all(self):
self.listbox.delete(0, tk.END)
self.file_paths.clear()
self.update_title()
def update_title(self):
count = len(self.file_paths)
self.title_label.config(text=f"Загрузка {f'({count})' if count else ''}")
def start_upload(self):
if not self.file_paths:
messagebox.showwarning("Ошибка", "Нет файлов")
return
self.upload_btn.pack_forget()
self.cancel_btn.pack(pady=0)
self.progress["value"] = 0
self.progress_text.config(text="0%")
self.current_file_label.config(text="")
self.uploading = True
self.status("Загрузка...")
threading.Thread(target=self.upload_files, daemon=True).start()
def cancel_upload(self):
self.uploading = False
self.root.after(100, self.finish_upload_with_cleanup)
def finish_upload_with_cleanup(self):
self.cancel_btn.pack_forget()
self.upload_btn.pack()
self.status("Отменено")
self.listbox.delete(0, tk.END)
self.file_paths.clear()
self.update_title()
def upload_files(self):
all_links = []
for filepath in self.file_paths:
if not self.uploading:
break
filename = os.path.basename(filepath)
self.root.after(0, lambda f=filename: self.current_file_label.config(text=f"Файл: {f}"))
self.root.after(0, lambda: self.progress_text.config(text="0%"))
try:
with open(filepath, 'rb') as f:
file_data = f.read()
boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
nl = '\r\n'
headers_part = (
f"--{boundary}{nl}"
f'Content-Disposition: form-data; name="password"{nl}{nl}'
f"{PASSWORD}{nl}"
f"--{boundary}{nl}"
f'Content-Disposition: form-data; name="files"; filename="{filename}"{nl}'
f"Content-Type: application/octet-stream{nl}{nl}"
).encode()
footer = f"{nl}--{boundary}--{nl}".encode()
total_size = len(headers_part) + len(file_data) + len(footer)
uploaded = 0
try:
value = int(self.speed_var.get())
if self.unit_var.get() == "МБ/с":
rate_kb = value * 1024
else:
rate_kb = value
except:
rate_kb = 25
def chunk_generator():
nonlocal uploaded
yield headers_part
uploaded += len(headers_part)
chunk_size = 1024
for i in range(0, len(file_data), chunk_size):
if not self.uploading:
return
chunk = file_data[i:i + chunk_size]
uploaded += len(chunk)
yield chunk
if rate_kb > 0:
delay = len(chunk) / (rate_kb * 1024)
time.sleep(delay)
percent = (uploaded / total_size) * 100
self.root.after(0, self.update_progress, int(percent))
if self.uploading:
yield footer
response = requests.post(
UPLOAD_URL,
data=chunk_generator(),
headers={'Content-Type': f'multipart/form-data; boundary={boundary}'},
timeout=300
)
if response.status_code == 200:
links = re.findall(r'https?://[^\s<>"{}|\\^`\[\]]+', response.text)
all_links.extend(links if links else [f"Готово: {filename}"])
else:
all_links.append(f"Ошибка: {filename}")
except Exception as e:
all_links.append(f"Ошибка: {filename}")
break
if all_links:
self.root.after(0, lambda: self.show_links_window(all_links))
try:
self.root.bell()
except:
pass
self.root.after(100, self.finish_upload_with_cleanup)
def update_progress(self, value):
self.progress["value"] = value
self.progress_text.config(text=f"{value}%")
def status(self, text):
self.status_bar.config(text=text)
def show_links_window(self, links):
win = tk.Toplevel(self.root)
win.title("Ссылки")
win.geometry("700x500")
win.resizable(True, True)
win.configure(bg="#1e1e1e")
tk.Label(win, text="Ссылки на файлы", font=("Segoe UI", 14, "bold"),
bg="#1e1e1e", fg="#00bfff").pack(pady=10)
main_frame = tk.Frame(win, bg="#1e1e1e")
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
canvas = tk.Canvas(main_frame, bg="#1e1e1e", highlightthickness=0)
scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
scrollable_frame = tk.Frame(canvas, bg="#1e1e1e")
scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw", width=600)
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
for link in links:
frame = tk.Frame(scrollable_frame, bg="#2d2d2d", pady=4)
frame.pack(fill=tk.X, pady=2, padx=10)
text_widget = tk.Text(frame, height=1, font=("Consolas", 10), bg="#2d2d2d", fg="#a0d8ff",
highlightthickness=1, highlightbackground="#007acc",
cursor="hand2")
text_widget.insert(tk.END, link)
text_widget.config(state=tk.DISABLED)
text_widget.pack(fill=tk.X)
def on_click(event, l=link):
self.root.clipboard_clear()
self.root.clipboard_append(l)
messagebox.showinfo("Готово", "Скопировано!", parent=win)
text_widget.bind("<Button-1>", on_click)
bottom_frame = tk.Frame(win, bg="#1e1e1e")
bottom_frame.pack(pady=20)
def copy_all():
self.root.clipboard_clear()
self.root.clipboard_append("\n".join(links))
messagebox.showinfo("Готово", "Все скопировано", parent=win)
tk.Button(bottom_frame, text="СКОПИРОВАТЬ ВСЕ",
bg="#28a745", fg="white",
font=("Segoe UI", 10, "bold"), width=30, command=copy_all,
cursor="hand2").pack()
win.transient(self.root)
win.grab_set()
if __name__ == "__main__":
root = tk.Tk()
app = ModernUploadApp(root)
root.mainloop()
Собрать данный проект можно скриптом build.sh
Bash:
#!/bin/bash
SCRIPT_NAME="uploader.py"
SPEC_FILE="uploader.spec"
if [ ! -f "$SCRIPT_NAME" ]; then
echo "Error: $SCRIPT_NAME not found!"
exit 1
fi
python3 -m venv env
source env/bin/activate
pip install --upgrade pip
pip install requests pyinstaller
pyinstaller --onefile --windowed "$SCRIPT_NAME"
if [ $? -ne 0 ]; then
deactivate
rm -rf env
exit 1
fi
deactivate
rm -rf env build
rm "$SPEC_FILE"
echo "Build completed. Executable is in 'dist' folder."