HTTP::Daemon, accept(), get_request(), send_file_response()

本文介绍了一个使用Perl语言实现的简单Web服务器模型。该模型能够处理客户端的基本请求,并通过send_file_response方法返回指定文件。代码展示了如何使用HTTP::Daemon模块创建服务器、监听请求并响应。

一个简单的,只能处理单一请求的Web服务器模型。

send_file_response()方法能把Client请求的文件传送过去。

#!/usr/bin/perl 

use HTTP:: Daemon; 

$| = 1; 
my $wwwroot = "/home/doc/"; 
my $d = HTTP:: Daemon->new || die; 
print "Perl Web-Server is running at: ", $d->url, " ...\n"; 

while (my $c = $d->accept) 
{    
   print $c "Welcome to Perl Web-Server<br>"; 

    if(my $r = $c->get_request) 
   {       
      print "Received : ", $r->url->path, "\n"; 
      $c->send_file_response($wwwroot.$r->url->path); 
    } 

    $c->close; 
}


# api_server.py import threading import time from flask import Flask, request, jsonify, Response from werkzeug.serving import make_server from flask_cors import CORS # --- 全局状态共享 --- current_status = { "is_listening": False, "is_tts_playing": False, "current_timeout": 8.0, "last_command_result": None, "timestamp": int(time.time()) } last_transcribed_text = "" from Progress.app import ( get_system_controller, get_task_executor, get_tts_engine, get_voice_recognizer, get_ai_assistant ) assistant = get_ai_assistant() executor = get_task_executor() tts_engine = get_tts_engine() recognizer = get_voice_recognizer() class CrossPlatformVoiceHandler: def __init__(self): self.supported_formats = { 'web': ['wav', 'mp3', 'ogg'], 'android': ['wav', 'mp3', 'aac', 'amr'], 'ios': ['wav', 'mp3', 'm4a', 'caf'], 'windows': ['wav', 'mp3', 'm4a'] } def process_voice_input(self, audio_data: dict, platform: str) -> dict: """处理来自不同平台的语音输入""" try: # 格式转换和预处理 processed_audio = self._convert_to_standard_format(audio_data, platform) # 调用语音识别 if platform == "web": text_result = self._web_voice_recognition(processed_audio) elif platform == "android": text_result = self._android_voice_recognition(processed_audio) elif platform == "ios": text_result = self._ios_voice_recognition(processed_audio) else: text_result = recognizer.listen_and_recognize() return { "success": True, "text": text_result, "platform": platform, "timestamp": int(time.time()) } except Exception as e: return { "success": False, "error": f"语音处理失败: {str(e)}" } def _convert_to_standard_format(self, audio_data, platform): """将不同平台的音频数据转换为标准格式""" if platform == "web": # Web Audio API 格式处理 return self._handle_web_audio(audio_data) elif platform == "android": return self._handle_android_audio(audio_data) elif platform == "ios": return self._handle_ios_audio(audio_data) else: return audio_data class EnhancedAPIServer: def __init__(self): self.app = Flask(__name__) CORS(self.app) self.server = None self.thread = None self.running = False self.voice_handler = CrossPlatformVoiceHandler() self._add_enhanced_routes() def _update_status(self, **kwargs): """更新全局状态""" current_status.update(kwargs) current_status["timestamp"] = int(time.time()) def _add_enhanced_routes(self): """注册所有增强API路由""" # 1. 健康检查接口 @self.app.route('/api/health', methods=['GET']) def health(): return jsonify({ "status": "ok", "service": "ai_assistant", "timestamp": int(time.time()) }) # 2. 跨平台语音处理接口 @self.app.route('/api/voice/process', methods=['POST']) def process_voice(): try: data = request.get_json() if not data or 'audio_data' not in data: return jsonify({"error": "缺少音频数据"}), 400 platform = data.get('platform', 'web') audio_format = data.get('format', 'wav') session_id = data.get('session_id') # 调用跨平台语音处理器 recognition_result = self.voice_handler.process_voice_input( data['audio_data'], platform ) # 如果识别成功,继续处理指令 if recognition_result.get('success') and recognition_result.get('text'): # 调用AI助手处理文本指令 decision = assistant.process_voice_command(recognition_result['text']) execution_result = executor.execute_task_plan(decision) # 构建完整响应 response_data = { "voice_recognition": recognition_result, "ai_processing": execution_result, "timestamp": int(time.time()) } return jsonify(response_data), 200 else: return jsonify({ "success": False, "error": "语音识别失败", "details": recognition_result }), 400 except Exception as e: return jsonify({ "success": False, "error": str(e) }), 500 # 3. 流式语音输入接口 @self.app.route('/api/voice/stream', methods=['POST']) def voice_stream(): """处理流式语音输入""" try: content_type = request.content_type if content_type == 'application/json': data = request.get_json() audio_data = data.get('audio_data') platform = data.get('platform', 'unknown') # 实时处理语音流 stream_processor = VoiceStreamProcessor() result = stream_processor.process_stream(audio_data, platform) return jsonify({ "status": "processing", "session_id": data.get('session_id'), "partial_result": result.get('partial_text', '') }) except Exception as e: return jsonify({"error": str(e)}), 500 # 4. 系统能力查询接口 @self.app.route('/api/capabilities', methods=['GET']) def get_capabilities(): """返回系统支持的功能和平台""" return jsonify({ "platforms": ["web", "android", "ios", "windows"], "audio_formats": { "web": ["wav", "mp3", "ogg"], "android": ["wav", "mp3", "aac", "amr"], "ios": ["wav", "mp3", "m4a", "caf"] }, "features": { "voice_recognition": True, "text_to_speech": True, "music_control": True, "file_operations": True, "system_control": True } }) # 5. 结果流式返回接口 @self.app.route('/api/result/stream', methods=['POST']) def stream_result(): """流式返回处理结果给前端""" try: data = request.get_json() session_id = data.get('session_id') callback_url = data.get('callback_url') return jsonify({ "streaming_supported": True, "protocols": ["websocket", "sse", "long_polling"] }) except Exception as e: return jsonify({"error": str(e)}), 500 # 6. 原有的命令处理接口(保持兼容) @self.app.route('/api/command', methods=['POST']) def handle_command(): try: data = request.get_json() if not data or 'text' not in data: return jsonify({ "success": False, "response_to_user": "未收到有效指令" }), 400 text = data['text'] context = data.get('context', {}) options = data.get('options', {}) should_speak = options.get('should_speak', True) return_plan = options.get('return_plan', False) decision = assistant.process_voice_command(text) result = executor.execute_task_plan(decision) ai_reply = result["message"] if not result["success"] and not ai_reply.startswith("抱歉"): ai_reply = f"抱歉,{ai_reply}" self._update_status( is_processing=True, last_command_result={"success": result["success"], "message": ai_reply, "operation": decision.get("action")} ) if should_speak: self._update_status(is_tts_playing=True) tts_engine.speak(ai_reply, async_mode=True) def _finish_tts(): time.sleep(1) self._update_status(is_tts_playing=False) threading.Thread(target=_finish_tts, daemon=True).start() response_data = { "success": result["success"], "response_to_user": ai_reply, "operation": decision.get("action"), "details": result, "should_speak": should_speak, "timestamp": int(time.time()), } if return_plan: response_data["plan"] = decision self._update_status(is_processing=False) return jsonify(response_data), 200 except Exception as e: return jsonify({ "success": False, "error": str(e), "timestamp": int(time.time()) }), 500 def start(self, host="127.0.0.1", port=5000, debug=False): """启动API服务(非阻塞)""" if self.running: print("⚠️ API服务器已在运行") return try: self.server = make_server(host, port, self.app) self.running = True def run_flask(): print(f"🌐 AI助手API已启动 → http://{host}:{port}/api") self.server.serve_forever() self.thread = threading.Thread(target=run_flask, daemon=True) self.thread.start() except Exception as e: print(f"❌ 启动API服务失败: {e}") raise def stop(self): """关闭API服务""" if not self.running: return print("🛑 正在关闭API服务...") try: self.server.shutdown() except: pass finally: self.running = False if self.thread: self.thread.join(timeout=3) if self.thread.is_alive(): print("⚠️ API服务线程未能及时退出") print("✅ API服务已关闭") # 语音流处理器 class VoiceStreamProcessor: def __init__(self): self.active_sessions = {} def process_stream(self, audio_data, platform): """处理实时语音流数据""" # 实时语音识别逻辑 partial_text = self._real_time_recognition(audio_data) return { "partial_text": partial_text, "is_final": False } def _real_time_recognition(self, audio_data): """实时语音识别实现""" # 这里可以集成WebRTC VAD等实时处理技术 return "正在识别中..." 分析一下这个代码的问题,并给出修改后的全代码 修改api工具让api工具能够调用各板块的功能完成几个接口, 1.从UI获取语音 2.将生成的语音返回给前端 需要的内容保留,不需要的删去,缺少的内容增加 注意,前端会从多种平台传入语音,window,Android,ios等等
最新发布
10-28
客户端 import os import socket import json import tkinter as tk from tkinter import filedialog, messagebox, ttk from datetime import datetime class FileClient: def __init__(self, root, server_host='localhost', server_port=65432): self.root = root self.root.title("网络文件管理系统 - 客户端") self.root.geometry("900x600") self.root.minsize(800, 500) # 设置中文字体 self.font = ('SimHei', 10) # 服务器连接信息 self.server_host = server_host self.server_port = server_port self.client_socket = None # 当前路径 self.current_path = "/" # 创建界面 self.create_widgets() self.connect_to_server() def create_widgets(self): """创建用户界面""" # 顶部导航栏 nav_frame = tk.Frame(self.root) nav_frame.pack(fill=tk.X, padx=5, pady=5) # 连接/断开按钮 self.connect_button = tk.Button(nav_frame, text="连接", font=self.font, command=self.toggle_connection) self.connect_button.pack(side=tk.LEFT, padx=(0, 5)) # 返回上级目录按钮 self.back_button = tk.Button(nav_frame, text="返回上级", font=self.font, command=self.go_up, state=tk.DISABLED) self.back_button.pack(side=tk.LEFT, padx=(0, 5)) # 刷新按钮 self.refresh_button = tk.Button(nav_frame, text="刷新", font=self.font, command=self.update_file_list, state=tk.DISABLED) self.refresh_button.pack(side=tk.LEFT, padx=(0, 5)) # 地址栏 self.path_var = tk.StringVar() self.path_var.set(self.current_path) self.path_entry = tk.Entry(nav_frame, textvariable=self.path_var, font=self.font, width=70) self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5)) self.path_entry.bind("<Return>", lambda event: self.navigate_to_path()) # 转到按钮 self.goto_button = tk.Button(nav_frame, text="转到", font=self.font, command=self.navigate_to_path, state=tk.DISABLED) self.goto_button.pack(side=tk.LEFT) # 中间文件列表区域 center_frame = tk.Frame(self.root) center_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 文件列表视图 columns = ("name", "type", "size", "modified") self.file_list = ttk.Treeview(center_frame, columns=columns, show="headings") self.file_list.heading("name", text="名称") self.file_list.heading("type", text="类型") self.file_list.heading("size", text="大小") self.file_list.heading("modified", text="修改日期") self.file_list.column("name", width=300) self.file_list.column("type", width=100) self.file_list.column("size", width=100) self.file_list.column("modified", width=200) self.file_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 添加滚动条 scrollbar = ttk.Scrollbar(center_frame, orient="vertical", command=self.file_list.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.file_list.configure(yscroll=scrollbar.set) # 绑定双击事件 self.file_list.bind("<Double-1>", self.on_double_click) # 底部状态栏 status_frame = tk.Frame(self.root) status_frame.pack(fill=tk.X, padx=5, pady=5) self.status_var = tk.StringVar() self.status_var.set("未连接到服务器") self.status_label = tk.Label(status_frame, textvariable=self.status_var, font=self.font) self.status_label.pack(side=tk.LEFT) # 右键菜单 self.menu = tk.Menu(self.root, tearoff=0) self.menu.add_command(label="打开", command=self.open_file, state=tk.DISABLED) self.menu.add_command(label="复制", command=self.copy_file, state=tk.DISABLED) self.menu.add_command(label="移动", command=self.move_file, state=tk.DISABLED) self.menu.add_command(label="删除", command=self.delete_file, state=tk.DISABLED) self.menu.add_separator() self.menu.add_command(label="新建文件夹", command=self.create_folder, state=tk.DISABLED) self.menu.add_command(label="重命名", command=self.rename_file, state=tk.DISABLED) self.file_list.bind("<Button-3>", self.show_menu) def connect_to_server(self): """连接到服务器""" try: self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client_socket.connect((self.server_host, self.server_port)) self.status_var.set(f"已连接到 {self.server_host}:{self.server_port}") self.connect_button.config(text="断开", command=self.disconnect_from_server) # 启用界面元素 self.back_button.config(state=tk.NORMAL) self.refresh_button.config(state=tk.NORMAL) self.goto_button.config(state=tk.NORMAL) # 启用右键菜单项 for i in range(self.menu.index('end') + 1): self.menu.entryconfig(i, state=tk.NORMAL) # 更新文件列表 self.update_file_list() except Exception as e: messagebox.showerror("连接错误", f"无法连接到服务器: {e}") self.client_socket = None def disconnect_from_server(self): """断开与服务器的连接""" if self.client_socket: try: self.client_socket.close() except Exception as e: print(f"关闭连接时出错: {e}") self.client_socket = None self.status_var.set("未连接到服务器") self.connect_button.config(text="连接", command=self.connect_to_server) # 禁用界面元素 self.back_button.config(state=tk.DISABLED) self.refresh_button.config(state=tk.DISABLED) self.goto_button.config(state=tk.DISABLED) # 禁用右键菜单项 for i in range(self.menu.index('end') + 1): self.menu.entryconfig(i, state=tk.DISABLED) def toggle_connection(self): """切换连接状态""" if self.client_socket: self.disconnect_from_server() else: self.connect_to_server() def send_request(self, command, path="", destination="", new_name=""): """向服务器发送请求并接收响应""" if not self.client_socket: messagebox.showerror("错误", "未连接到服务器") return None try: # 构建请求 request = { 'command': command, 'path': path, 'destination': destination, 'new_name': new_name } # 发送请求 serialized = json.dumps(request).encode('utf-8') self.client_socket.sendall(serialized) # 接收响应 response_data = self.client_socket.recv(4096).decode('utf-8') if not response_data: messagebox.showerror("错误", "与服务器的连接已断开") self.disconnect_from_server() return None response = json.loads(response_data) return response except Exception as e: messagebox.showerror("通信错误", f"与服务器通信时出错: {e}") self.disconnect_from_server() return None def update_file_list(self): """更新文件列表""" if not self.client_socket: return # 清空文件列表 for item in self.file_list.get_children(): self.file_list.delete(item) # 更新当前路径显示 self.path_var.set(self.current_path) self.status_var.set(f"当前路径: {self.current_path}") # 发送列表请求 response = self.send_request('list', self.current_path) if not response or not response.get('success'): messagebox.showerror("错误", response.get('message', "获取文件列表失败")) return # 填充文件列表 for item in response.get('items', []): self.file_list.insert("", "end", values=( item['name'], item['type'], item['size'], item['modified'] )) def go_up(self): """导航到上级目录""" if self.current_path == "/": return parent_path = os.path.dirname(self.current_path.rstrip("/")) if parent_path == "": parent_path = "/" self.current_path = parent_path self.update_file_list() def navigate_to_path(self): """导航到地址栏中输入的路径""" new_path = self.path_var.get() response = self.send_request('open', new_path) if not response or not response.get('success'): messagebox.showerror("错误", response.get('message', "无效的目录路径")) self.path_var.set(self.current_path) return if response.get('is_file', False): messagebox.showinfo("信息", f"这是一个文件: {os.path.basename(new_path)}") self.path_var.set(self.current_path) else: self.current_path = new_path self.update_file_list() def on_double_click(self, event): """双击打开文件夹或文件""" selection = self.file_list.selection() if not selection: return item = selection[0] item_name = self.file_list.item(item, "values")[0] item_path = os.path.join(self.current_path, item_name) response = self.send_request('open', item_path) if not response or not response.get('success'): messagebox.showerror("错误", response.get('message', "无法打开")) return if response.get('is_file', False): messagebox.showinfo("信息", f"文件: {item_name}\n大小: {response['size']}\n修改时间: {response['modified']}") else: self.current_path = item_path self.update_file_list() def show_menu(self, event): """显示右键菜单""" selection = self.file_list.identify_row(event.y) if selection: self.file_list.selection_set(selection) self.menu.post(event.x_root, event.y_root) def get_selected_item_path(self): """获取选中项的完整路径""" selection = self.file_list.selection() if not selection: return None item = selection[0] item_name = self.file_list.item(item, "values")[0] return os.path.join(self.current_path, item_name) def open_file(self): """打开选中的文件或文件夹""" item_path = self.get_selected_item_path() if not item_path: return response = self.send_request('open', item_path) if not response or not response.get('success'): messagebox.showerror("错误", response.get('message', "无法打开")) return if response.get('is_file', False): messagebox.showinfo("信息", f"文件: {os.path.basename(item_path)}\n大小: {response['size']}\n修改时间: {response['modified']}") else: self.current_path = item_path self.update_file_list() def copy_file(self): """复制选中的文件或文件夹""" source_path = self.get_selected_item_path() if not source_path: return destination = filedialog.askdirectory(title="选择目标文件夹") if not destination: return # 转换为服务器路径格式 server_destination = self._convert_to_server_path(destination) response = self.send_request('copy', source_path, server_destination) if response and response.get('success'): messagebox.showinfo("成功", response.get('message', "复制完成")) self.update_file_list() else: messagebox.showerror("错误", response.get('message', "复制失败")) def move_file(self): """移动选中的文件或文件夹""" source_path = self.get_selected_item_path() if not source_path: return destination = filedialog.askdirectory(title="选择目标文件夹") if not destination: return # 转换为服务器路径格式 server_destination = self._convert_to_server_path(destination) response = self.send_request('move', source_path, server_destination) if response and response.get('success'): messagebox.showinfo("成功", response.get('message', "移动完成")) self.update_file_list() else: messagebox.showerror("错误", response.get('message', "移动失败")) def delete_file(self): """删除选中的文件或文件夹""" item_path = self.get_selected_item_path() if not item_path: return item_name = os.path.basename(item_path) if messagebox.askyesno("确认删除", f"确定要删除 {item_name} 吗?\n此操作不可恢复!"): response = self.send_request('delete', item_path) if response and response.get('success'): messagebox.showinfo("成功", response.get('message', "删除完成")) self.update_file_list() else: messagebox.showerror("错误", response.get('message', "删除失败")) def create_folder(self): """创建新文件夹""" def create(): folder_name = entry.get() if folder_name: new_folder_path = os.path.join(self.current_path, folder_name) response = self.send_request('create_folder', new_folder_path) if response and response.get('success'): messagebox.showinfo("成功", response.get('message', "文件夹创建完成")) top.destroy() self.update_file_list() else: messagebox.showerror("错误", response.get('message', "创建文件夹失败")) else: messagebox.showerror("错误", "文件夹名称不能为空") # 创建对话框 top = tk.Toplevel(self.root) top.title("新建文件夹") top.geometry("300x120") top.resizable(False, False) top.transient(self.root) top.grab_set() frame = tk.Frame(top, padx=20, pady=20) frame.pack(fill=tk.BOTH, expand=True) label = tk.Label(frame, text="文件夹名称:", font=self.font) label.pack(anchor=tk.W) entry = tk.Entry(frame, font=self.font) entry.pack(fill=tk.X, pady=(5, 15)) entry.focus_set() button_frame = tk.Frame(frame) button_frame.pack(fill=tk.X) ok_button = tk.Button(button_frame, text="确定", font=self.font, command=create) ok_button.pack(side=tk.RIGHT, padx=(5, 0)) cancel_button = tk.Button(button_frame, text="取消", font=self.font, command=top.destroy) cancel_button.pack(side=tk.RIGHT) def rename_file(self): """重命名选中的文件或文件夹""" item_path = self.get_selected_item_path() if not item_path: return old_name = os.path.basename(item_path) def rename(): new_name = entry.get() if new_name and new_name != old_name: response = self.send_request('rename', item_path, new_name=new_name) if response and response.get('success'): messagebox.showinfo("成功", response.get('message', "重命名完成")) top.destroy() self.update_file_list() else: messagebox.showerror("错误", response.get('message', "重命名失败")) else: messagebox.showerror("错误", "名称无效或未更改") # 创建对话框 top = tk.Toplevel(self.root) top.title("重命名") top.geometry("300x120") top.resizable(False, False) top.transient(self.root) top.grab_set() frame = tk.Frame(top, padx=20, pady=20) frame.pack(fill=tk.BOTH, expand=True) label = tk.Label(frame, text="新名称:", font=self.font) label.pack(anchor=tk.W) entry = tk.Entry(frame, font=self.font) entry.pack(fill=tk.X, pady=(5, 15)) entry.insert(0, old_name) entry.selection_range(0, tk.END) entry.focus_set() button_frame = tk.Frame(frame) button_frame.pack(fill=tk.X) ok_button = tk.Button(button_frame, text="确定", font=self.font, command=rename) ok_button.pack(side=tk.RIGHT, padx=(5, 0)) cancel_button = tk.Button(button_frame, text="取消", font=self.font, command=top.destroy) cancel_button.pack(side=tk.RIGHT) def _convert_to_server_path(self, local_path): """将本地路径转换为服务器路径格式(简化处理)""" # 注意:实际应用中可能需要更复杂的路径映射逻辑 # 这里简单地将路径分隔符统一为正斜杠 return local_path.replace("\\", "/") if __name__ == "__main__": root = tk.Tk() app = FileClient(root) root.mainloop() 服务端 import os import socket import json import threading import datetime # 添加缺失的导入 import shutil # 添加缺失的导入 import time # 添加缺失的导入 class FileServer: def __init__(self, host='localhost', port=65432, buffer_size=4096): self.host = host # 服务器主机 self.port = port # 服务器端口 self.buffer_size = buffer_size # 缓冲区大小 self.root_dir = os.path.abspath(".") # 服务器文件根目录 self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server_socket.bind((self.host, self.port)) self.server_socket.listen(5) self.running = False self.clients = [] def start(self): """启动服务器""" self.running = True print(f"服务器启动: {self.host}:{self.port}") print(f"根目录: {self.root_dir}") # 启动监听线程 accept_thread = threading.Thread(target=self._accept_clients) accept_thread.daemon = True accept_thread.start() def stop(self): """停止服务器""" self.running = False if self.server_socket: try: # 创建一个临时连接以唤醒accept() temp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) temp_socket.connect((self.host, self.port)) temp_socket.close() except Exception as e: print(f"关闭服务器时出错: {e}") finally: self.server_socket.close() self.server_socket = None print("服务器已停止") def _accept_clients(self): """接受客户端连接""" while self.running: try: client_socket, client_address = self.server_socket.accept() print(f"客户端连接: {client_address}") client_handler = threading.Thread(target=self._handle_client, args=(client_socket,)) client_handler.daemon = True client_handler.start() except Exception as e: if self.running: print(f"接受客户端连接时出错: {e}") def _handle_client(self, client_socket): """处理客户端请求""" try: while True: # 接收请求 request = self._receive_data(client_socket) if not request: break response = self._process_request(request) self._send_data(client_socket, response) except Exception as e: print(f"处理客户端请求时出错: {e}") finally: client_socket.close() print("客户端断开连接") def _receive_data(self, client_socket): """接收并解析客户端数据""" try: data = client_socket.recv(self.buffer_size).decode('utf-8') if not data: return None return json.loads(data) except Exception as e: print(f"接收数据时出错: {e}") return None def _send_data(self, client_socket, data): """序列化并发送数据到客户端""" try: serialized = json.dumps(data).encode('utf-8') client_socket.sendall(serialized) except Exception as e: print(f"发送数据时出错: {e}") def _process_request(self, request): """处理客户端请求""" command = request.get('command') path = request.get('path', '') destination = request.get('destination', '') new_name = request.get('new_name', '') # 规范化路径,防止路径遍历攻击 safe_path = self._sanitize_path(path) safe_destination = self._sanitize_path(destination) try: if command == 'list': return self._list_directory(safe_path) elif command == 'open': return self._open_path(safe_path) elif command == 'copy': return self._copy_file(safe_path, safe_destination) elif command == 'move': return self._move_file(safe_path, safe_destination) elif command == 'delete': return self._delete_file(safe_path) elif command == 'create_folder': return self._create_folder(safe_path) elif command == 'rename': return self._rename_file(safe_path, new_name) else: return {'success': False, 'message': f"未知命令: {command}"} except Exception as e: return {'success': False, 'message': str(e)} def _sanitize_path(self, path): """规范化路径,防止路径遍历攻击""" # 确保路径从服务器根目录开始 full_path = os.path.abspath(os.path.join(self.root_dir, path)) # 验证路径是否在服务器根目录内 if not full_path.startswith(self.root_dir): raise PermissionError("访问被拒绝") return full_path def _list_directory(self, path): """列出目录内容""" if not os.path.exists(path): return {'success': False, 'message': "路径不存在"} if not os.path.isdir(path): return {'success': False, 'message': "不是目录"} items = [] try: for item in os.listdir(path): item_path = os.path.join(path, item) try: stats = os.stat(item_path) modified_time = datetime.datetime.fromtimestamp(stats.st_mtime).strftime("%Y-%m-%d %H:%M:%S") if os.path.isdir(item_path): item_type = "文件夹" item_size = "" else: item_type = "文件" # 格式化文件大小 size = stats.st_size for unit in ['B', 'KB', 'MB', 'GB']: if size < 1024.0: item_size = f"{size:.2f} {unit}" break size /= 1024.0 items.append({ 'name': item, 'type': item_type, 'size': item_size, 'modified': modified_time }) except Exception as e: print(f"无法获取 {item} 的信息: {e}") # 排序:文件夹在前,文件在后 items.sort(key=lambda x: (0 if x['type'] == '文件夹' else 1, x['name'].lower())) return {'success': True, 'items': items} except Exception as e: return {'success': False, 'message': f"无法读取目录: {e}"} def _open_path(self, path): """打开文件或目录""" if not os.path.exists(path): return {'success': False, 'message': "路径不存在"} if os.path.isdir(path): return self._list_directory(path) else: # 对于文件,返回文件信息 try: stats = os.stat(path) modified_time = datetime.datetime.fromtimestamp(stats.st_mtime).strftime("%Y-%m-%d %H:%M:%S") size = stats.st_size for unit in ['B', 'KB', 'MB', 'GB']: if size < 1024.0: item_size = f"{size:.2f} {unit}" break size /= 1024.0 return { 'success': True, 'is_file': True, 'name': os.path.basename(path), 'size': item_size, 'modified': modified_time } except Exception as e: return {'success': False, 'message': f"无法获取文件信息: {e}"} def _copy_file(self, source, destination): """复制文件或目录""" if not os.path.exists(source): return {'success': False, 'message': "源路径不存在"} try: if os.path.isdir(source): # 确保目标目录不存在 if os.path.exists(destination): return {'success': False, 'message': "目标路径已存在"} shutil.copytree(source, destination) else: # 如果目标是目录,使用源文件名 if os.path.isdir(destination): destination = os.path.join(destination, os.path.basename(source)) shutil.copy2(source, destination) return {'success': True, 'message': "复制完成"} except Exception as e: return {'success': False, 'message': f"复制失败: {e}"} def _move_file(self, source, destination): """移动文件或目录""" if not os.path.exists(source): return {'success': False, 'message': "源路径不存在"} try: # 如果目标是目录,使用源文件名 if os.path.isdir(destination): destination = os.path.join(destination, os.path.basename(source)) shutil.move(source, destination) return {'success': True, 'message': "移动完成"} except Exception as e: return {'success': False, 'message': f"移动失败: {e}"} def _delete_file(self, path): """删除文件或目录""" if not os.path.exists(path): return {'success': False, 'message': "路径不存在"} try: if os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) return {'success': True, 'message': "删除完成"} except Exception as e: return {'success': False, 'message': f"删除失败: {e}"} def _create_folder(self, path): """创建新文件夹""" if os.path.exists(path): return {'success': False, 'message': "路径已存在"} try: os.makedirs(path) return {'success': True, 'message': "文件夹创建完成"} except Exception as e: return {'success': False, 'message': f"创建文件夹失败: {e}"} def _rename_file(self, path, new_name): """重命名文件或目录""" if not os.path.exists(path): return {'success': False, 'message': "路径不存在"} directory = os.path.dirname(path) new_path = os.path.join(directory, new_name) if os.path.exists(new_path): return {'success': False, 'message': "新名称已存在"} try: os.rename(path, new_path) return {'success': True, 'message': "重命名完成"} except Exception as e: return {'success': False, 'message': f"重命名失败: {e}"} if __name__ == "__main__": server = FileServer() try: server.start() # 保持主线程运行 while True: time.sleep(1) except KeyboardInterrupt: server.stop() 服务器无法连接到客户端
06-25
那么根据我的提供的代码帮我添加一下,在HTTP模式下登录成功,对该用户名添加cookie和httponly from utils import USER_FILE, UPLOAD_DIR, DOWNLOAD_DIR, load_users, save_user, check_user, check_user_md5 from bottle import Bottle, static_file, redirect, request, run, template, BaseRequest, response from bottle_auth import AuthPlugin import hashlib import os import ctypes import tarfile import tempfile from routes_en import setup_routes_en, cleanup_tar_files import threading import time USER_FILE = '/mnt/usrfs/webTest/user_auth.txt' UPLOAD_DIR = '/mnt/usrfs/upload' DOWNLOAD_DIR = '/mnt/usrfs/' BaseRequest.MEMFILE_MAX = 1024 * 1024 * 15 # 15MB # 5. 自定义 AuthPlugin,支持md5密码 class MD5AuthPlugin(AuthPlugin): def __init__(self, users): super().__init__(users) def check_auth(self, username, password): # password是明文,转md5后比对 pwd_md5 = hashlib.md5(password.encode()).hexdigest() return check_user_md5(username, pwd_md5) def require(self, f): def check_auth(*args, **kwargs): username = request.auth[0] if request.auth else None password = request.auth[1] if request.auth else None if not username or not password: response.headers['WWW-Authenticate'] = 'Basic realm=\"Login Required\"' response.status = 401 return "Authentication required" if not self.check_auth(username, password): response.headers['WWW-Authenticate'] = 'Basic realm=\"Login Required\"' response.status = 401 return "Authentication failed" return f(*args, **kwargs) return check_auth # 6. 创建 Bottle 应用 app = Bottle() users = load_users() auth = MD5AuthPlugin(users) app.install(auth) setup_routes_en(app,auth) # 启动清理线程 if __name__ == '__main__': # 如果没有用户文件,自动创建一个默认用户 if not os.path.exists(USER_FILE): save_user('root', 'root') # 使用最简单的配置 t = threading.Thread(target=cleanup_tar_files, daemon=True) t.start() app.run(host='0.0.0.0', port=80, debug=False, reloader=False)以上是main;from utils import USER_FILE, UPLOAD_DIR, DOWNLOAD_DIR, check_user from bottle import static_file, redirect, request, template, response from bottle import template from page_generators_en import get_basic_info_html_en, get_dev_status_html_en import tarfile import os import time from vtysh_cmd_send import VtyshCommandExecutor USER_FILE = '/mnt/usrfs/webTest/user_auth.txt' UPLOAD_DIR = '/mnt/usrfs/upload' DOWNLOAD_DIR = '/mnt/usrfs/' def setup_routes_en(app, auth): @app.route('/www/baseMsg_en.asp') def basic_info_www_en(): html = get_basic_info_html_en() return template('basic_info_en', basic_info_html_en=html) @app.route('/www/devStatus_en.asp') def dev_status_www_en(): html = get_dev_status_html_en() return template('dev_status_en', dev_status_html_en=html) @app.route('/www/ipaddr_en.asp') def ip_config_page(): return template('ip_address_en') @app.route('/api/execute_ip_config', method='POST') def execute_ip_config(): try: # 获取前端发送的配置 config = request.json # 验证配置 if not config or 'network' not in config: return {'success': False, 'error': 'Invalid configuration'} # 使用VtyshCommandExecutor执行配置 from vtysh_cmd_send import VtyshCommandExecutor executor = VtyshCommandExecutor("../bin/vtysh") result = executor.execute_config(config) return result except Exception as e: return {'success': False, 'error': str(e)} # def file_root_en(app): @app.route('/') def index(): return redirect('/www/index1/index_en.html') @app.route('/download_file/<path:path>') def download_file(path): print(f"DEBUG: 下载请求路径: {path}") # 调试信息 abs_file = os.path.abspath(os.path.join(DOWNLOAD_DIR, path)) print(f"DEBUG: 绝对文件路径: {abs_file}") # 调试信息 # 检查DOWNLOAD_DIR是否存在 if not os.path.exists(DOWNLOAD_DIR): print(f"DEBUG: DOWNLOAD_DIR不存在") # 调试信息 return "DOWNLOAD_DIR不存在" if not abs_file.startswith(os.path.abspath(DOWNLOAD_DIR)): print(f"DEBUG: 路径安全检查失败") # 调试信息 return "非法路径" # 检查文件或目录是否存在 if not os.path.exists(abs_file): print(f"DEBUG: 文件/目录不存在: {abs_file}") # 调试信息 return f"文件/目录不存在: {abs_file}" # 如果是目录,创建tar文件 if os.path.isdir(abs_file): print(f"DEBUG: 开始打包目录: {abs_file}")# 调试信息 try: # 在DOWNLOAD_DIR中创建tar文件,而不是临时目录 tar_filename = os.path.basename(abs_file) + '.tar' tar_path = os.path.join(DOWNLOAD_DIR, tar_filename) print(f"DEBUG: tar文件路径: {tar_path}") # 调试信息 # 创建tar文件 with tarfile.open(tar_path, 'w') as tar: tar.add(abs_file, arcname=os.path.basename(abs_file)) # 检查tar文件是否创建成功 if os.path.exists(tar_path): file_size = os.path.getsize(tar_path) print(f"DEBUG: tar文件创建成功,大小: {file_size} 字节") # 调试信息 else: print(f"DEBUG: tar文件创建失败") # 调试信息 return "tar文件创建失败" print(f"DEBUG: 目录打包完成: {tar_path}") # 调试信息 print(f"DEBUG: 下载文件名: {tar_filename}") # 调试信息 # 使用static_file返回tar文件 # 设置响应头 response.set_header('Content-Type', 'application/x-tar') response.set_header('Content-Disposition', f'attachment; filename=\"{tar_filename}\"') response.set_header('Content-Length', str(file_size)) response.set_header('Accept-Ranges', 'bytes') # 直接 return 生成器 return generate_with_timeout(tar_path, timeout=60) except Exception as e: print(f"DEBUG: 打包目录失败: {e}") # 调试信息 return f"打包目录失败: {e}" # 如果是文件,直接下载 if not os.path.isfile(abs_file): print(f"DEBUG: 不是普通文件: {abs_file}") # 调试信息 return f"不是普通文件: {abs_file}" # 检查文件权限 if not os.access(abs_file, os.R_OK): print(f"DEBUG: 文件没有读取权限: {abs_file}") # 调试信息 return f"文件没有读取权限: {abs_file}" print(f"DEBUG: 文件存在,开始下载: {abs_file}") # 调试信息 filename = os.path.basename(abs_file) print(f"DEBUG: 下载文件名: {filename}") # 调试信息 # 使用正确的MIME类型和文件名 return static_file(filename, root=os.path.dirname(abs_file), download=filename) @app.route('/<filepath:path>') def serve_static(filepath): return static_file(filepath, root='/mnt/usrfs/') # 表单登录 @app.route('/action/login', method='POST') def do_login(): username = request.forms.get('username') password = request.forms.get('password') if check_user(username, password): return redirect('/www/index_en.htm') else: return "Login failed!" # 受保护路由 @app.route('/protected') @auth.require def protected(): return "你已通过 HTTP Basic Auth 认证!" @app.route('/www/web_upload_file_en.asp', method=['GET', 'POST']) def web_upload_file_en(): if request.method == 'GET': return template('upload_en') # 注意这里是 upload._en else: upload_file = request.files.get('uploadfile') if not upload_file or not upload_file.filename: return "<p style='color:red'>没有选择文件!</p>" save_path = os.path.join(UPLOAD_DIR, upload_file.filename) upload_file.save(save_path, overwrite=True) return f"<p style='color:green'>文件已上传到: {save_path}</p><a href='/www/web_upload_file_en.asp'>返回</a>" @app.route('/filebrowser') @app.route('/filebrowser/<path:path>') def filebrowser(path=''): print(f"DEBUG: 访问路径: {path}") # 调试信息 abs_dir = os.path.abspath(os.path.join(DOWNLOAD_DIR, path)) print(f"DEBUG: 绝对路径: {abs_dir}") # 调试信息 if not abs_dir.startswith(os.path.abspath(DOWNLOAD_DIR)): print(f"DEBUG: 路径安全检查失败") # 调试信息 return "非法路径" if not os.path.isdir(abs_dir): print(f"DEBUG: 目录不存在: {abs_dir}") # 调试信息 return "目录不存在" items = [] for fname in sorted(os.listdir(abs_dir)): fpath = os.path.join(abs_dir, fname) is_dir = os.path.isdir(fpath) items.append({'name': fname, 'is_dir': is_dir}) parent = os.path.dirname(path) if path else None print(f"DEBUG: 父目录: {parent}") # 调试信息 return template('download_en', items=items, cur_path=path, parent=parent) def generate_with_timeout(file_path, timeout=60): start_time = time.time() with open(file_path, 'rb') as f: while True: if time.time() - start_time > timeout: break chunk = f.read(1024 * 1024) if not chunk: break yield chunk def cleanup_tar_files(): while True: now = time.time() for fname in os.listdir(DOWNLOAD_DIR): if fname.endswith('.tar'): fpath = os.path.join(DOWNLOAD_DIR, fname) try: if os.path.isfile(fpath): # 删除2分钟以前的tar文件 if now - os.path.getmtime(fpath) > 120: os.remove(fpath) print(f"DEBUG: 自动清理tar文件: {fpath}") except Exception as e: print(f"DEBUG: 清理tar文件失败: {e}") time.sleep(60) # 每分钟检查一次 以上是route部分
07-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值