Skip to content

Commit 1ba50b5

Browse files
committed
feat: 完善 /download 路由
1 parent 878286d commit 1ba50b5

File tree

6 files changed

+55
-15
lines changed

6 files changed

+55
-15
lines changed

core/types.py renamed to core/classes.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from dataclasses import dataclass
2-
from typing import List
2+
from typing import List, Dict, Any
33
from abc import ABC, abstractmethod
44
import io
5+
from aiohttp import web
56
import tqdm
67

78

@@ -42,3 +43,7 @@ async def writeFile(
4243
@abstractmethod
4344
async def getMissingFiles(files: FileList, pbar: tqdm) -> FileList:
4445
pass
46+
47+
@abstractmethod
48+
async def express(hash: str, request: web.Request, response: web.StreamResponse) -> Dict[str, Any]:
49+
pass

core/cluster.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from core.scheduler import *
44
from core.exceptions import ClusterIdNotSetError, ClusterSecretNotSetError
55
from core.storages import getStorages
6-
from core.types import FileInfo, FileList, AgentConfiguration
6+
from core.classes import FileInfo, FileList, AgentConfiguration
7+
from core.router import Router
78
from core.i18n import locale
9+
from aiohttp import web
810
from tqdm import tqdm
911
import toml
1012
import zstandard as zstd
@@ -233,8 +235,10 @@ async def downloadFile(
233235
self.failed_filelist.files.append(file)
234236

235237
async def setupExpress(self, https: bool) -> None:
236-
# todo
237-
pass
238+
logger.tinfo("cluster.info.router.creating")
239+
app = web.Application
240+
Router(https, app)
241+
238242

239243
async def init(self) -> None:
240244
await asyncio.gather(*(storage.init() for storage in self.storages))

core/web.py renamed to core/router.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
from functools import wraps
22
from core.config import Config
3+
from core.classes import Storage
4+
from typing import Any, List
5+
from aiohttp import web
6+
import random
37
import aiohttp
4-
import aiohttp.web
58
import asyncio
69
import base64
710
import hashlib
811
import time
912

1013
class Router:
11-
def __init__(self, https: bool, app: aiohttp.web.Application) -> None:
14+
def __init__(self, https: bool, app: web.Application) -> None:
1215
self.https = https
1316
self.app = app
1417
self.secret = Config.get('cluster.secret')
@@ -27,16 +30,20 @@ async def _():
2730
pass
2831

2932
@route("/download/{hash}")
30-
async def _(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
31-
response = aiohttp.web.Response
33+
async def _(self, request: web.Request, storages: List[Storage]) -> web.Response | web.StreamResponse:
3234
def check_sign(hash: str, secret: str, query: dict) -> bool:
3335
if not (s := query.get('s')) or not (e := query.get('e')): return False
3436
sign = base64.urlsafe_b64encode(hashlib.sha1(f"{secret}{hash}{e}".encode('utf-8')).digest()).decode('utf-8').rstrip('=')
3537
return sign == s and time.time() < int(e, 36)
3638

3739
hash = request.match_info.get('hash').lower()
38-
async with aiohttp.ClientSession() as session:
39-
valid = check_sign(hash, self.secret, request.query)
40-
if not valid:
41-
return response(text="invalid sign", status=403)
42-
response.
40+
valid = check_sign(hash, self.secret, request.query)
41+
if not valid:
42+
return web.Response(text="invalid sign", status=403)
43+
response = web.StreamResponse(status=200)
44+
response.headers['x-bmclapi-hash'] = hash
45+
storage = random.randint(0, len(storages) - 1)
46+
data = storages[storage].express(hash, request, response)
47+
return response
48+
49+

core/storages/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from core.types import Storage
1+
from core.classes import Storage
22
from core.storages.local import LocalStorage
33
from core.config import Config
44
from typing import List

core/storages/local.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from core.types import Storage, FileInfo, FileList
1+
from core.classes import Storage, FileInfo, FileList
22
from core.logger import logger
33
from core.i18n import locale
4+
from aiohttp import web
5+
from typing import Any, Dict
46
from tqdm import tqdm
57
import os
68
import io
@@ -75,3 +77,24 @@ async def check_file(file: FileInfo, pbar: tqdm) -> bool:
7577
file for file, is_missing in zip(files.files, results) if is_missing
7678
]
7779
return FileList(files=missing_files)
80+
81+
async def express(self, hash: str, request: web.Request, response: web.StreamResponse) -> Dict[str, Any]:
82+
path = os.path.join(self.path, hash[:2], hash)
83+
if not os.path.exists(path):
84+
response.set_status(404, 'File not found')
85+
return {'bytes': 0, 'hits': 0}
86+
file_size = os.path.getsize(path)
87+
response.content_length = file_size
88+
response.content_type = 'application/octet-stream'
89+
response.headers['Cache-Control'] = 'max-age=2592000'
90+
response.prepare(request)
91+
92+
try:
93+
transport = request.transport
94+
socket = transport.get_extra_info('socket')
95+
with open(path, 'rb') as f:
96+
await asyncio.get_event_loop().sendfile(socket, f, 0, file_size)
97+
return {'bytes': file_size, 'hits': 1}
98+
99+
except Exception:
100+
return {'bytes': 0, 'hits': 0}

i18n/zh_cn.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"cluster.success.sync_files.downloaded": "成功下载所有文件!",
2121
"cluster.error.sync_files.retry": "无法下载所有文件,将在 ${retry}s 后重试。",
2222
"cluster.error.sync_files.failed": "无法下载所有文件,已达到最高重试次数。",
23+
"cluster.info.router.creating": "正在创建路由……",
2324
"cluster.tqdm.desc.get_missing": "获取缺失文件中",
2425
"cluster.tqdm.desc.sync_files": "同步文件中",
2526
"cluster.tqdm.unit.files": " 个文件",

0 commit comments

Comments
 (0)