DriveDAV:一个挂载网盘为WebDAV的轻量级工具

AI大语言模型

一个挂载网盘为 WebDAV 的轻量级工具,基于Python WsgiDAV开发,默认支持阿里云盘,可扩展其他网盘。

支持添加多网盘账户,支持配置文件加密,快速配置WebDav用户,SSL证书,服务端口。

安装与使用

安装DriveDAV:

git clone https://gitee.com/beizigen/drivedav.git
cd drivedav
pipx install ./

卸载:

pipx uninstall drivedav

配置网盘:

drivedav config

服务类命令:

drivedav start | stop | restart | status

WebDAV地址:

http://127.0.0.1:8080/[name]

配置文件加密后,可设置全局变量免除每次启动需要输入密码:

DRIVEDAV_CONFIG_PASS = "Password"

网盘接口开发

DriveDAV易扩展,如需支持其他网盘,实现DriveBackend网盘后端接口即可。

网盘接口:

在drives下创建网盘目录,例如:alipan

__init__.py中导出接口,名称必须为:Backend,并定义NAME常量。示例:

from .backend import AlipanBackend as Backend

NAME = "阿里云盘"
__all__ = ["Backend"]

DriveBackend抽象类:

from ...core.drive_backend import DriveBackend

DriveBackend中的抽象方法必须实现,可参考alipan/backend.py的实现。

方法描述
config网盘的配置过程,例如:OAuth授权获取Token。获取API所需的信息后,以字典数据返回,系统自动保存到配置文件。
注意:当is_new参数为True时,是用户新增网盘过程,DriveConfig中没有配置数据;is_new参数为False时,可使用DriveConfig读取旧配置信息。
get_meta获取资源元数据,包括目录和文件,成功返回字典数据,资源不存在返回None。
数据结构如下:
- name:资源名称,字符串值或None;
- size:文件大小,目录可不需要,单位:字节;
- created:资源创建时间的Unix timestamp;
- modified:资源最后修改时间的Unix timestamp;
- etag:文件的SHA,没有返回None;
- mime:文件的MIME类型;
- is_dir:是否为目录,布尔值:True | False。
list_dir获取目录下子项名称列表:[name, name2…]
make_dir创建目录,不用考虑多级,系统会自动处理多级的情况。
read_file读取文件流,返回requests的有效响应。requests请求时,使用参数stream=True
open_writer上传文件,实现DriveUploadHandle抽象类,返回上传对象。
- write:客户端上传的文件流通过该方法写入;
- close:文件流写入完成时调用。
delete删除资源
move移动/重命名资源
copy复制资源

可用工具:

配置文件管理:DriveBackend类注入了配置文件管理工具。

def __init__(self, drive_config: "DriveConfig")

self._drive_config = drive_config

# 加载当前网盘所有配置项
self._cfg = self._drive_config.load()

# 保存当前网盘所有配置项
self._drive_config.save(self._cfg)

缓存:适当缓存API请求结果可减少API请求次数。

from ...utils.cache import Cache

......

self._cache = Cache(max_size=10000, default_ttl=600)

self._cache.set(key, value, ttl=900)
self._cache.get(key)
self._cache.delete(key)

节流器:API请求通常有QPS限制,阿里云盘为180毫秒,超过限制常返回429错误。节流器默认0.2秒间隔,可自行配置适合的间隔时间。

from ...utils.throttle import Throttler

# 至少0.2秒后执行
throttler = Throttler(min_interval=0.2)
throttler.wait()

一个包装请求方法的示例,使用了节流器:

self._session = requests.Session()
self._throttler = Throttler(min_interval=0.2)
......

def _request(self, method, url, **kwargs)-> dict[str, Any]:
    self._throttler.wait()

    access_token = ""
    headers = kwargs.pop("headers", {}) or {}
    headers["Content-Type"] = "application/json"
    headers["Authorization"] = f"Bearer {access_token}"

    try:
        resp = self._session.request(method, url, headers=headers, **kwargs)
        resp.raise_for_status()
    except requests.RequestException as e:
        raise AlipanError.parse_response(getattr(e, "response", None), e)

    return resp.json()

错误处理:将网盘API错误转换为系统错误。

from ...core.drive_error import *

# 资源不存在时返回FileNotFound错误
raise FileNotFound("NotFound.File", "文件不存在...", "响应ID")
错误类描述
FileNotFound资源不存在,常见状态码:404
TokenExpiredToken过期,常见状态码:401
RateLimitExceededAPI限制,常见状态码:429
PermissionDenied请求被拒绝,常见状态码:403
ServiceUnavailable服务不可用,常见状态码:500、502、503
DriveError其他错误

时间戳转换:将ISO8601和数字时间格式转换为 UTC 秒级时间戳。

from ...utils.helpers import to_utc_timestamp

modified_str = file_meta.get("updated_at")
modified = to_utc_timestamp(modified_str)

获取文件SHA1 哈希值:

from ...utils.helpers import sha1_file

content_hash = sha1_file(local_path)

获取文件前1K SHA1 哈希值:

from ...utils.helpers import sha1_file

pre_hash = sha1_head(local_path, head_size=1024)

支持与反馈

由于精力有限,测试不够充分,可能有未知BUG,欢迎反馈,我会尽力抽时间修复。

如果觉得好用,感谢使用我的推荐开通阿里云盘VIP:

https://www.alipan.com/cpx/member?userCode=MTAzNjcx

AI大语言模型