diff --git a/static/fsys.py b/static/fsys.py index d3f52a0..5048bb3 100644 --- a/static/fsys.py +++ b/static/fsys.py @@ -60,6 +60,9 @@ SSL_ENABLED = file_config['ssl'].get('enabled', 'false').lower() == 'true' SSL_CERT_FILE = file_config['ssl'].get('cert_file', 'cert.pem') SSL_KEY_FILE = file_config['ssl'].get('key_file', 'key.pem') +# 视图文件名:根路径与目录访问时固定返回此静态文件,由前端 JS 调用 /api/filelist 拉取数据 +VIEW_FILE = '__view.html' + # ==================== 视频文件支持 ==================== VIDEO_MIME_TYPES = { '.mp4': 'video/mp4', '.webm': 'video/webm', '.ogg': 'video/ogg', @@ -106,29 +109,30 @@ def get_file_list(): try: if not FILESYSTEM_ENABLED: return jsonify({'error': '文件系统未启用'}), 403 - + requested_path = request.args.get('path', '') full_path = safe_path_join(HTML_ROOT_DIR, requested_path) - + if full_path is None: return jsonify({'error': '无效的路径'}), 403 - + if not os.path.exists(full_path) or not os.path.isdir(full_path): return jsonify({'error': '目录不存在'}), 404 - + items = [] for item in os.listdir(full_path): - if item == '__pycache__': + # 过滤系统项目:以双下划线开头的文件和文件夹(如 __pycache__、__view.html、__icons 等) + if item.startswith('__'): continue - + item_path = os.path.join(full_path, item) item_stat = os.stat(item_path) is_dir = os.path.isdir(item_path) - + item_rel_path = os.path.join(requested_path, item) if requested_path else item - + file_type = 'dir' if is_dir else ('video' if is_video_file(item) else ('audio' if is_audio_file(item) else 'file')) - + items.append({ 'name': item, 'type': file_type, @@ -137,9 +141,9 @@ def get_file_list(): 'size_formatted': format_file_size(item_stat.st_size) if not is_dir else '', 'modified': datetime.datetime.fromtimestamp(item_stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S') }) - + items.sort(key=lambda x: (x['type'] != 'dir', x['name'].lower())) - + return jsonify({ 'success': True, 'path': requested_path or '/', @@ -155,33 +159,33 @@ def stream_video(file_path): try: if not FILESYSTEM_ENABLED: return jsonify({'error': '文件系统未启用'}), 403 - + full_path = safe_path_join(HTML_ROOT_DIR, file_path) if full_path is None or not os.path.exists(full_path) or not os.path.isfile(full_path): return jsonify({'error': '文件不存在'}), 404 - + if not is_video_file(full_path) and not is_audio_file(full_path): return jsonify({'error': '不支持的文件类型'}), 400 - + file_size = os.path.getsize(full_path) mime_type = get_media_mime_type(full_path) - + range_header = request.headers.get('Range', None) - + if range_header: byte_range = range_header.replace('bytes=', '').split('-') start = int(byte_range[0]) if byte_range[0] else 0 end = int(byte_range[1]) if len(byte_range) > 1 and byte_range[1] else file_size - 1 - + if start >= file_size: return jsonify({'error': '请求范围超出文件大小'}), 416 - + length = end - start + 1 - + with open(full_path, 'rb') as f: f.seek(start) data = f.read(length) - + response = Response(data, 206, mimetype=mime_type, direct_passthrough=True) response.headers.add('Content-Range', f'bytes {start}-{end}/{file_size}') response.headers.add('Accept-Ranges', 'bytes') @@ -194,342 +198,38 @@ def stream_video(file_path): logger.error(f"Video streaming error: {str(e)}") return jsonify({'error': str(e)}), 500 -# ==================== 极简版HTML页面(使用字符串拼接,避免花括号问题) ==================== -def get_html_template(path, breadcrumb_html): - """生成HTML页面""" - return f''' - - - - - 文件浏览 - - - -
-

📁 文件浏览

-
- 🏠 - 🔄 -
-
- - - - - -
- 📊 共 0 项 - 📁 0 - 📄 0 - 🎬 0 - 🎵 0 -
- -
-
- ⏳ -
加载中...
-
-
- -
- - - -''' - # ==================== 主路由 ==================== -@app.route('/', defaults={'path': ''}) +@app.route('/') +def serve_view(): + """根路径固定返回 __view.html 静态文件,由前端 JS 调用 /api/filelist 拉取文件系统信息""" + if not FILESYSTEM_ENABLED: + return "文件系统未启用", 403 + view_path = os.path.join(HTML_ROOT_DIR, VIEW_FILE) + if not os.path.exists(view_path): + return f"视图文件 {VIEW_FILE} 不存在", 500 + return send_file(view_path, mimetype='text/html') + @app.route('/') -def serve_file(path=''): +def serve_file(path): try: if not FILESYSTEM_ENABLED: return "文件系统未启用", 403 - + full_path = safe_path_join(HTML_ROOT_DIR, path) if full_path is None: return "无效的路径", 403 - + if not os.path.exists(full_path): return "路径不存在", 404 - + # 文件处理 if os.path.isfile(full_path): if request.args.get('download') == 'true': return send_file(full_path, as_attachment=True, download_name=os.path.basename(full_path)) - + if is_video_file(full_path) or is_audio_file(full_path): return send_file(full_path, mimetype=get_media_mime_type(full_path)) - + # 普通文件 with open(full_path, 'rb') as f: content = f.read() @@ -541,23 +241,13 @@ def serve_file(path=''): '.pdf': 'application/pdf', '.svg': 'image/svg+xml', '.ico': 'image/x-icon' } return Response(content, mimetype=mime_map.get(ext, 'application/octet-stream')) - - # 目录处理 - 生成面包屑 - breadcrumb = '' - if path: - parts = path.split('/') - current = '' - for i, part in enumerate(parts): - if i == len(parts) - 1: - breadcrumb += f'›{part}' - else: - current += part + '/' - breadcrumb += f'›{part}' - - # 使用函数生成HTML,避免字符串格式化问题 - html = get_html_template(path, breadcrumb) - return Response(html, mimetype='text/html') - + + # 目录处理:返回 __view.html,由前端 JS 根据 URL 路径调用 /api/filelist 拉取文件列表 + view_path = os.path.join(HTML_ROOT_DIR, VIEW_FILE) + if os.path.exists(view_path): + return send_file(view_path, mimetype='text/html') + return "目录无法浏览", 404 + except Exception as e: logger.error(f"Error: {str(e)}") import traceback @@ -575,7 +265,7 @@ def health_check(): if __name__ == '__main__': host = file_config['server']['host'] port = int(file_config['server']['port']) - + print("\n" + "="*50) print("📁 文件托管服务器 (极简版)") print("="*50) @@ -589,3 +279,4 @@ if __name__ == '__main__': app.run(host=host, port=port, ssl_context=(SSL_CERT_FILE, SSL_KEY_FILE), debug=False) else: app.run(host=host, port=port, debug=False) + diff --git a/static/public/__view.html b/static/public/__view.html new file mode 100644 index 0000000..e7a6318 --- /dev/null +++ b/static/public/__view.html @@ -0,0 +1,331 @@ + + + + + + 文件浏览 + + + +
+

📁 文件浏览

+
+ 🏠 + 🔄 +
+
+ + + + + +
+ 📊 共 0 项 + 📁 0 + 📄 0 + 🎬 0 + 🎵 0 +
+ +
+
+ ⏳ +
加载中...
+
+
+ +
+ + + + + diff --git a/static/public/dnfs.html b/static/public/dnfs.html index 96c94ed..77857bb 100644 --- a/static/public/dnfs.html +++ b/static/public/dnfs.html @@ -39,3 +39,4 @@ +