fix(alist): support openlist rapid upload headers (#5897)

This commit is contained in:
Album
2026-06-05 06:50:20 +08:00
committed by GitHub
parent fc8933c648
commit a9b1f7e9c9
2 changed files with 73 additions and 0 deletions
+27
View File
@@ -1,3 +1,4 @@
import hashlib
import json import json
import time import time
from datetime import datetime from datetime import datetime
@@ -708,6 +709,7 @@ class Alist(StorageBase, metaclass=WeakSingleton):
# 获取文件大小 # 获取文件大小
target_name = new_name or path.name target_name = new_name or path.name
target_path = Path(fileitem.path) / target_name target_path = Path(fileitem.path) / target_name
stat = path.stat()
# 初始化进度回调 # 初始化进度回调
progress_callback = transfer_process(path.as_posix()) progress_callback = transfer_process(path.as_posix())
@@ -718,6 +720,9 @@ class Alist(StorageBase, metaclass=WeakSingleton):
headers.setdefault("Content-Type", "application/octet-stream") headers.setdefault("Content-Type", "application/octet-stream")
headers.setdefault("As-Task", str(task).lower()) headers.setdefault("As-Task", str(task).lower())
headers.setdefault("File-Path", encoded_path) headers.setdefault("File-Path", encoded_path)
headers.setdefault("Content-Length", str(stat.st_size))
headers.setdefault("Last-Modified", str(int(stat.st_mtime * 1000)))
headers.update(self.__get_upload_hash_headers(path))
# 创建自定义的文件流,支持进度回调 # 创建自定义的文件流,支持进度回调
class ProgressFileReader: class ProgressFileReader:
@@ -783,6 +788,28 @@ class Alist(StorageBase, metaclass=WeakSingleton):
logger.error(f"【OpenList】上传文件 {path} 失败:{e}") logger.error(f"【OpenList】上传文件 {path} 失败:{e}")
return None return None
@staticmethod
def __get_upload_hash_headers(path: Path) -> dict:
"""
计算 OpenList 秒传所需的文件哈希请求头。
"""
md5_hash = hashlib.md5()
sha1_hash = hashlib.sha1()
sha256_hash = hashlib.sha256()
with open(path, "rb") as file_handler:
while True:
chunk = file_handler.read(1024 * 1024)
if not chunk:
break
md5_hash.update(chunk)
sha1_hash.update(chunk)
sha256_hash.update(chunk)
return {
"X-File-Md5": md5_hash.hexdigest(),
"X-File-Sha1": sha1_hash.hexdigest(),
"X-File-Sha256": sha256_hash.hexdigest(),
}
def detail(self, fileitem: schemas.FileItem) -> Optional[schemas.FileItem]: def detail(self, fileitem: schemas.FileItem) -> Optional[schemas.FileItem]:
""" """
获取文件详情 获取文件详情
+46
View File
@@ -1,5 +1,7 @@
import hashlib
import unittest import unittest
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from app.modules.filemanager.storages import alist as alist_module from app.modules.filemanager.storages import alist as alist_module
@@ -137,3 +139,47 @@ class AlistStorageTest(unittest.TestCase):
self.assertEqual("alist", target.storage) self.assertEqual("alist", target.storage)
self.assertEqual("file", target.type) self.assertEqual("file", target.type)
self.assertEqual(1024, target.size) self.assertEqual(1024, target.size)
def test_upload_sends_hash_headers_for_rapid_upload(self):
"""
OpenList 上传应附带文件哈希头,供服务端尝试秒传。
"""
with TemporaryDirectory() as temp_dir:
local_path = Path(temp_dir) / "rapid.bin"
content = b"moviepilot-openlist-rapid-upload"
local_path.write_bytes(content)
upload_dir = FileItem(
storage="alist",
type="dir",
path="/library/",
name="library",
basename="library",
)
uploaded_item = FileItem(
storage="alist",
type="file",
path="/library/rapid.bin",
name="rapid.bin",
basename="rapid",
extension="bin",
size=len(content),
)
request_utils = MagicMock()
request_utils.put_res.return_value = _FakeResponse(
{"code": 200, "message": "success", "data": None}
)
with patch.object(Alist, "get_conf", return_value={"url": "http://openlist.test", "token": "token"}):
with patch.object(alist_module, "RequestUtils", return_value=request_utils) as request_utils_factory:
with patch.object(self.storage, "_delay_get_item", return_value=uploaded_item):
result = self.storage.upload(upload_dir, local_path)
self.assertEqual(uploaded_item, result)
request_utils.put_res.assert_called_once()
headers = request_utils_factory.call_args.kwargs["headers"]
self.assertEqual(hashlib.md5(content).hexdigest(), headers["X-File-Md5"])
self.assertEqual(hashlib.sha1(content).hexdigest(), headers["X-File-Sha1"])
self.assertEqual(hashlib.sha256(content).hexdigest(), headers["X-File-Sha256"])
self.assertEqual(str(len(content)), headers["Content-Length"])
self.assertEqual("application/octet-stream", headers["Content-Type"])
self.assertEqual("/library/rapid.bin", headers["File-Path"])