diff --git a/app.py b/app.py index fba355f..26c1ca1 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ from waitress import serve from flask import Flask, render_template, request, redirect, url_for, jsonify, session, send_from_directory, g +from flask_compress import Compress from datetime import datetime import os import logging @@ -12,6 +13,10 @@ import pandas as pd app = Flask(__name__, template_folder='templates') app.secret_key = os.urandom(24) # 设置会话密钥 +# 初始化Gzip压缩 +compress = Compress() +compress.init_app(app) + # 配置日志 if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) @@ -156,10 +161,78 @@ def contact(): def serve_log_file(filename): return send_from_directory(LOG_DIR, filename) -# 服务下载目录中的文件 +# 服务下载目录中的文件 - 优化版本 @app.route('/downloads/') def serve_download_file(filename): - return send_from_directory('downloads', filename) + import os + from flask import request, Response + from werkzeug.datastructures import Range + from werkzeug.exceptions import RequestedRangeNotSatisfiable + + file_path = os.path.join('downloads', filename) + + # 检查文件是否存在 + if not os.path.isfile(file_path): + return jsonify({"status": "error", "message": "文件未找到"}), 404 + + # 获取文件大小和最后修改时间 + file_size = os.path.getsize(file_path) + last_modified = os.path.getmtime(file_path) + + # 处理Range请求(断点续传) + range_header = request.headers.get('Range') + if range_header: + try: + range_obj = Range.parse_range_header(range_header, file_size) + if range_obj.ranges: + # 支持单个范围请求 + start, end = range_obj.ranges[0] + length = end - start + 1 if end is not None else file_size - start + + def generate(): + with open(file_path, 'rb') as f: + f.seek(start) + remaining = length + while remaining > 0: + chunk_size = min(8192, remaining) + chunk = f.read(chunk_size) + if not chunk: + break + yield chunk + remaining -= len(chunk) + + response = Response( + generate(), + 206, # Partial Content + mimetype='application/vnd.android.package-archive', + direct_passthrough=True + ) + response.headers['Content-Range'] = f'bytes {start}-{start + length - 1}/{file_size}' + response.headers['Content-Length'] = str(length) + response.headers['Accept-Ranges'] = 'bytes' + + # 设置缓存头 - 1小时缓存 + response.headers['Cache-Control'] = 'public, max-age=3600' + response.headers['Last-Modified'] = last_modified + + return response + except (ValueError, RequestedRangeNotSatisfiable): + # 如果Range请求无效,返回完整文件 + pass + + # 普通文件下载(无Range请求或Range请求无效) + response = send_from_directory('downloads', filename) + + # 设置缓存头 - 1小时缓存 + response.headers['Cache-Control'] = 'public, max-age=3600' + response.headers['Last-Modified'] = last_modified + response.headers['Accept-Ranges'] = 'bytes' + + # 对于APK文件设置正确的MIME类型 + if filename.lower().endswith('.apk'): + response.headers['Content-Type'] = 'application/vnd.android.package-archive' + + return response # 全局错误处理 @app.errorhandler(404) diff --git a/static/images/apk.svg b/static/images/apk.svg index 852374b..01b8c91 100644 --- a/static/images/apk.svg +++ b/static/images/apk.svg @@ -1,3 +1,3 @@ - - + + diff --git a/static/images/不同操作系统下载图标 (15).png b/static/images/不同操作系统下载图标 (15).png new file mode 100644 index 0000000..b03c3ef Binary files /dev/null and b/static/images/不同操作系统下载图标 (15).png differ diff --git a/templates/contact.html b/templates/contact.html index 2fddf8c..de0d9ed 100644 --- a/templates/contact.html +++ b/templates/contact.html @@ -3,12 +3,12 @@ {% block title %}{{ t('pricing') if t('pricing') else '收费' }}{% endblock %} {% block content %} -
+
diff --git a/templates/download.html b/templates/download.html index bd26a25..5efe354 100644 --- a/templates/download.html +++ b/templates/download.html @@ -3,12 +3,12 @@ {% block title %}{{ t('download') if t('download') else '下载' }}{% endblock %} {% block content %} -
+
@@ -86,8 +86,8 @@