#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 图文匹配检测服务 完整的Web服务器实现 """ import http.server import socketserver import os import json import urllib.parse from pathlib import Path import time import requests import logging # 配置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class ImageTextMatchingHandler(http.server.SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): # 设置静态文件目录 super().__init__(*args, directory="web", **kwargs) def end_headers(self): # 添加CORS头 self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') super().end_headers() def do_OPTIONS(self): # 处理预检请求 self.send_response(200) self.end_headers() def do_POST(self): """处理POST请求""" if self.path == '/upload': self.handle_upload() elif self.path == '/api/image_text_matching': self.handle_matching_api() else: self.send_error(404) def do_GET(self): """处理GET请求""" if self.path == '/api/health': # 健康检查端点 self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() response = {"status": "ok", "service": "image_text_matching"} self.wfile.write(json.dumps(response).encode()) else: # 静态文件处理 super().do_GET() def handle_upload(self): """处理文件上传""" try: content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) # 解析multipart/form-data content_type = self.headers['Content-Type'] if 'multipart/form-data' in content_type: boundary = content_type.split('boundary=')[1].encode() parts = post_data.split(b'--' + boundary) for part in parts: if b'Content-Disposition: form-data' in part and b'filename=' in part: # 提取文件名 lines = part.split(b'\r\n') filename = "unknown.jpg" for line in lines: if b'Content-Disposition' in line: try: filename_start = line.find(b'filename="') + 10 filename_end = line.find(b'"', filename_start) filename = line[filename_start:filename_end].decode() except: pass break # 提取文件内容 content_start = part.find(b'\r\n\r\n') + 4 content_end = part.rfind(b'\r\n') if content_end == -1: content_end = len(part) file_content = part[content_start:content_end] if len(file_content) > 0: # 保存文件 upload_dir = Path("uploads") upload_dir.mkdir(exist_ok=True) timestamp = str(int(time.time() * 1000)) file_extension = filename.split('.')[-1] if '.' in filename else 'jpg' save_filename = f"upload_{timestamp}.{file_extension}" file_path = upload_dir / save_filename with open(file_path, 'wb') as f: f.write(file_content) # 返回文件URL file_url = f"http://localhost:8080/uploads/{save_filename}" response = {"success": True, "url": file_url} self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(response).encode()) return self.send_error(400, "Bad Request - No valid file found") except Exception as e: logger.error(f"Upload error: {e}") self.send_error(500, f"Internal Server Error: {str(e)}") def handle_matching_api(self): """处理图文匹配API请求""" try: content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) # 解析JSON数据 data = json.loads(post_data.decode('utf-8')) # 调用图文匹配检测服务 result = self.call_matching_service(data) # 返回结果 self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(result, ensure_ascii=False).encode('utf-8')) except json.JSONDecodeError as e: logger.error(f"JSON decode error: {e}") self.send_error(400, "Invalid JSON data") except Exception as e: logger.error(f"Matching API error: {e}") error_response = { "code": 500, "message": f"服务器内部错误: {str(e)}", "result": [] } self.send_response(500) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(error_response, ensure_ascii=False).encode('utf-8')) def call_matching_service(self, data): """调用外部图文匹配检测服务""" api_url = "http://localhost:29500/v1/image_text_matching" try: # 准备请求数据 request_data = { "illustration_url": data.get("illustration_url", ""), "caption_text": data.get("caption_text", ""), "context_info": data.get("context_info", "") } logger.info(f"Calling matching service with data: {request_data}") # 调用外部服务 response = requests.post(api_url, json=request_data, timeout=30) if response.status_code == 200: result = response.json() logger.info(f"Matching service response: {result}") return result else: logger.error(f"Matching service error: {response.status_code} - {response.text}") return { "code": response.status_code, "message": f"外部服务错误: {response.text}", "result": [] } except requests.exceptions.RequestException as e: logger.error(f"Request to matching service failed: {e}") return { "code": 503, "message": f"无法连接到图文匹配检测服务 (localhost:29500): {str(e)}", "result": [] } except Exception as e: logger.error(f"Unexpected error in matching service call: {e}") return { "code": 500, "message": f"服务调用失败: {str(e)}", "result": [] } def setup_directories(): """创建必要的目录结构""" directories = ["web", "uploads"] for directory in directories: os.makedirs(directory, exist_ok=True) print(f"✓ 创建目录: {directory}") def create_index_html(): """创建前端HTML文件""" html_content = ''' 图文匹配检测服务

🔍 图文匹配检测服务

上传图片并输入相关文本,检测图片与文本的匹配度

⚙️ 服务配置

服务状态: 检测中...

📸 上传图片

📁
点击或拖拽上传图片
支持 JPG、PNG、JPEG 格式

✏️ 文本信息

📊 检测结果

''' with open("web/index.html", "w", encoding="utf-8") as f: f.write(html_content) print("✓ 创建前端文件: web/index.html") def start_server(port=8080): """启动Web服务器""" print("正在初始化图文匹配检测服务...") # 设置目录结构 setup_directories() # 创建前端文件 create_index_html() print("\n" + "="*50) print("🚀 图文匹配检测服务启动中...") print("="*50) # 启动服务器 with socketserver.TCPServer(("", port), ImageTextMatchingHandler) as httpd: server_address = ("", port) print(f"✓ 服务器绑定地址: http://localhost:{port}") print(f"✓ 静态文件目录: web/") print(f"✓ 上传文件目录: uploads/") print(f"✓ 外部API地址: http://localhost:29500") try: print(f"\n🌐 服务器启动成功!") print(f"📱 请在浏览器中访问: http://localhost:{port}") print(f"🔧 管理界面: http://localhost:{port}/api/health") print("\n" + "="*50) print("按 Ctrl+C 停止服务器") print("="*50) httpd.serve_forever() except KeyboardInterrupt: print("\n\n⏹️ 正在停止服务器...") httpd.shutdown() print("✓ 服务器已停止") if __name__ == "__main__": import sys # 解析命令行参数 port = 8080 if len(sys.argv) > 1: try: port = int(sys.argv[1]) except ValueError: print("❌ 端口号必须是数字") sys.exit(1) # 启动服务器 start_server(port)