1- from flask import Flask , request , jsonify , send_from_directory , stream_with_context , Response
1+ from flask import Flask , request , jsonify , send_from_directory , stream_with_context , Response , abort
22from flask_cors import CORS
33import threading
44import time
@@ -24,9 +24,15 @@ def __init__(self):
2424 # 配置跨域请求
2525 CORS (self .app )
2626
27+ # 使用绝对路径以避免 CWD (当前工作目录) 不同导致的问题
28+ # .../DogApp2/dog
29+ current_dir = os .path .dirname (os .path .abspath (__file__ ))
30+ # .../DogApp2
31+ root_dir = os .path .dirname (current_dir )
32+
2733 # 服务器配置
28- self .UPLOAD_FOLDER = 'uploads'
29- self .ASSETS_FOLDER = os .path .join ('dog' , 'assets' )
34+ self .UPLOAD_FOLDER = os . path . join ( root_dir , 'uploads' )
35+ self .ASSETS_FOLDER = os .path .join (current_dir , 'assets' )
3036 self .ALLOWED_IMAGE_EXTENSIONS = {'png' , 'jpg' , 'jpeg' , 'gif' , 'bmp' }
3137 self .MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB
3238
@@ -80,8 +86,8 @@ def __init__(self):
8086 self .notification_history_lock = Lock ()
8187 self .notification_next_id = 1
8288
83- # 简单账号存储(明文,保存在 uploads /users.json)
84- self .users_filename = 'users.json'
89+ # 简单账号存储(明文,保存在 dog /users.json)
90+ self .users_file_path = os . path . join ( current_dir , 'users.json' )
8591
8692 # 日程提醒推送相关
8793 self .schedule_notifier_stop = threading .Event ()
@@ -152,17 +158,28 @@ def save_assets_json(self, data, filename):
152158
153159 # ========== 简易账号存储(明文,仅演示用途) ==========
154160 def load_users (self ):
155- data = self .load_json_data (self .users_filename )
156- if not data :
157- return {'users' : []}
158- if isinstance (data , list ):
159- return {'users' : data }
160- if isinstance (data , dict ) and 'users' in data :
161- return {'users' : data .get ('users' , [])}
161+ try :
162+ with self .file_lock :
163+ if os .path .exists (self .users_file_path ):
164+ with open (self .users_file_path , 'r' , encoding = 'utf-8' ) as f :
165+ data = json .load (f )
166+ if isinstance (data , list ):
167+ return {'users' : data }
168+ if isinstance (data , dict ) and 'users' in data :
169+ return {'users' : data .get ('users' , [])}
170+ except Exception as e :
171+ logger .error (f"加载用户数据失败: { e } " )
162172 return {'users' : []}
163173
164174 def save_users (self , users ):
165- return self .save_json_data (users , self .users_filename )
175+ try :
176+ with self .file_lock :
177+ with open (self .users_file_path , 'w' , encoding = 'utf-8' ) as f :
178+ json .dump (users , f , ensure_ascii = False , indent = 4 )
179+ return True
180+ except Exception as e :
181+ logger .error (f"保存用户数据失败: { e } " )
182+ return False
166183
167184 def get_video_stream_generator (self , video_path ):
168185 """生成视频流,循环播放"""
@@ -616,6 +633,17 @@ def get_image(filename):
616633 return send_from_directory (self .app .config ['UPLOAD_FOLDER' ], filename )
617634 except FileNotFoundError :
618635 return jsonify ({'message' : '图片未找到' }), 404
636+
637+ # 访问 assets 下的静态资源(含记忆库图片)
638+ @self .app .route ('/assets/<path:filename>' )
639+ def get_asset (filename ):
640+ try :
641+ safe_path = os .path .normpath (filename )
642+ if safe_path .startswith ('..' ):
643+ return abort (400 )
644+ return send_from_directory (self .app .config ['ASSETS_FOLDER' ], safe_path )
645+ except FileNotFoundError :
646+ return jsonify ({'message' : '资源未找到' }), 404
619647
620648 # GPS数据上传路由
621649 @self .app .route ('/upload_gps' , methods = ['POST' ])
@@ -876,18 +904,38 @@ def update_photo_info():
876904 photo_id = data .get ('photo_id' )
877905 description = data .get ('description' , '' )
878906 audio_file = data .get ('audio_file' , '' )
907+ title = data .get ('title' , '' )
908+ event_date = data .get ('event_date' , '' )
909+ image_file = data .get ('image_file' , '' )
910+ location = data .get ('location' , '' )
911+ tags = data .get ('tags' , [])
912+ people = data .get ('people' , [])
913+ emotion = data .get ('emotion' , '' )
914+
915+ if isinstance (tags , str ):
916+ tags = [t .strip () for t in tags .split (',' ) if t .strip ()]
917+ if isinstance (people , str ):
918+ people = [p .strip () for p in people .split (',' ) if p .strip ()]
879919
880920 if not photo_id :
881921 return jsonify ({'message' : '缺少photo_id字段' }), 400
882922
883923 # 加载现有数据
884924 photo_info = self .load_assets_json ('photo_info.json' )
925+ existing = photo_info .get (photo_id , {})
885926
886927 # 更新或添加
887928 photo_info [photo_id ] = {
888- 'description' : description ,
929+ 'title' : title or existing .get ('title' , '' ),
930+ 'description' : description or existing .get ('description' , '' ),
931+ 'event_date' : event_date or existing .get ('event_date' , '' ),
889932 'update_time' : datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' ),
890- 'audio_file' : audio_file
933+ 'image_file' : image_file or existing .get ('image_file' , '' ),
934+ 'audio_file' : audio_file or existing .get ('audio_file' , '' ),
935+ 'location' : location or existing .get ('location' , '' ),
936+ 'tags' : tags or existing .get ('tags' , []),
937+ 'people' : people or existing .get ('people' , []),
938+ 'emotion' : emotion or existing .get ('emotion' , '' )
891939 }
892940
893941 if self .save_assets_json (photo_info , 'photo_info.json' ):
@@ -960,7 +1008,8 @@ def upload_photo_image():
9601008 return jsonify ({
9611009 'message' : '照片上传成功!' ,
9621010 'photo_id' : photo_id ,
963- 'file_path' : filename
1011+ 'file_path' : filename ,
1012+ 'asset_path' : f"photo_detector/{ filename } "
9641013 }), 200
9651014 else :
9661015 return jsonify ({'message' : '不支持的文件类型' }), 400
@@ -1700,21 +1749,21 @@ def internal_error(e):
17001749
17011750
17021751
1703- def run_server (self ):
1752+ def run_server (self , port = 5000 ):
17041753 """运行Flask服务器"""
17051754 print ("启动机器狗控制服务器..." )
17061755 print (f"上传目录: { os .path .abspath (self .app .config ['UPLOAD_FOLDER' ])} " )
1707- print ("服务器地址: http://0.0.0.0:5000 " )
1756+ print (f "服务器地址: http://0.0.0.0:{ port } " )
17081757
17091758 # 使用多线程模式运行
1710- self .app .run (host = '0.0.0.0' , port = 5000 , debug = False , threaded = True )
1759+ self .app .run (host = '0.0.0.0' , port = port , debug = False , threaded = True )
17111760
1712- def start (self ):
1761+ def start (self , port = 5000 ):
17131762 """启动服务器线程"""
1714- server_thread = threading .Thread (target = self .run_server )
1763+ server_thread = threading .Thread (target = self .run_server , kwargs = { 'port' : port } )
17151764 server_thread .daemon = True
17161765 server_thread .start ()
1717- print ("机器狗API服务器已启动在端口5000 " )
1766+ print (f"机器狗API服务器已启动在端口 { port } " )
17181767
17191768# 集成到现有的机器狗主程序
17201769def main ():
0 commit comments