# -*- coding: utf-8 -*- r"""修"大文件下载失败"。 现象: 公网下载 0.19MB 的 APK -> 200 ✓ 公网下载 46KB 的服务器包 -> 200 ✓ 公网下载 19MB 的被控端包 -> 000 ✗ 原因: 1. relay 原来是 blob = f.read_bytes() 一次性读进内存再 write 出去。 19MB 在 460MB 的设备上勉强,但更要命的是—— 2. **Cloudflare 有 100 秒的源站响应上限**。 设备上行慢(4G,还带丢包重传),19MB 传不完就被掐断。 小文件几百毫秒就完了,所以只有大文件挂。 改法: A. 改成**分块流式**发送(64KB 一块),边读边发、及时 flush —— 至少不会因为攒内存而卡住,也让数据尽早开始流动。 B. 管理页的下载列表里,每个文件旁边加一个「从 Gitee 下」的链接。 走 Gitee 的 CDN 比走这条 4G 隧道快得多,大文件应该走那边。 C. 前端按大小给提示:超过 5MB 的文件,标一句"建议从 Gitee 下"。 """ from __future__ import annotations import ast import sys from pathlib import Path RELAY = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\relay" r"\relay.py") # ---------- A. 流式发送 ---------- OLD_SERVE = ''' try: blob = target.read_bytes() except OSError as exc: self._json(500, {"ok": False, "error": f"读取失败: {exc}"}) return if safe.lower().endswith(".apk"): content_type = "application/vnd.android.package-archive" elif safe.lower().endswith(".zip"): content_type = "application/zip" elif safe.lower().endswith(".txt"): content_type = "text/plain; charset=utf-8" else: content_type = "application/octet-stream" self.send_response(200) self.send_header("Content-Type", content_type) self.send_header("Content-Length", str(len(blob))) # 文件名可能含中文,用 RFC 5987 编码 quoted = urllib.parse.quote(safe) self.send_header( "Content-Disposition", f"attachment; filename*=UTF-8''{quoted}") self.send_header("Cache-Control", "no-store") self.end_headers() self.wfile.write(blob) log(f"下载:{safe}({len(blob)} 字节)") return''' NEW_SERVE = ''' try: size = target.stat().st_size except OSError as exc: self._json(500, {"ok": False, "error": f"读不到: {exc}"}) return if safe.lower().endswith(".apk"): content_type = "application/vnd.android.package-archive" elif safe.lower().endswith(".zip"): content_type = "application/zip" elif safe.lower().endswith(".txt"): content_type = "text/plain; charset=utf-8" else: content_type = "application/octet-stream" self.send_response(200) self.send_header("Content-Type", content_type) self.send_header("Content-Length", str(size)) # 文件名可能含中文,用 RFC 5987 编码 quoted = urllib.parse.quote(safe) self.send_header( "Content-Disposition", f"attachment; filename*=UTF-8''{quoted}") self.send_header("Cache-Control", "no-store") self.end_headers() # 分块流式发送,不要一次性 read_bytes 再 write —— # 大文件(19MB)那样会先攒满内存、数据也迟迟不开始流动; # 而 Cloudflare 有 100 秒的源站响应上限,越晚开始发越容易被掐。 sent = 0 try: with target.open("rb") as stream: while True: chunk = stream.read(65536) if not chunk: break self.wfile.write(chunk) sent += len(chunk) self.wfile.flush() except (BrokenPipeError, ConnectionResetError): # 客户端/边缘主动断开(比如大文件超时),正常现象 log(f"下载中断:{safe}(已发 {sent}/{size} 字节)") return except OSError as exc: log(f"下载出错:{safe}(已发 {sent}/{size} 字节){exc}") return log(f"下载:{safe}({sent} 字节)") return''' # ---------- B/C. 前端加 Gitee 链接与大文件提示 ---------- OLD_JS = ''' body.innerHTML = files.map(f => {{ const isApk = f.name.toLowerCase().endsWith('.apk'); const tag = isApk ? '手表 APK' : '整包'; return '