webdav简单客户端,支持创建文件,列目录,读取文件,写入文件,删除文件,移动/复制文件,判断文件是否存在等
Version vom
Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/554239/1687012/WebDAVClient.js
主要功能
这个 WebDAV 客户端实现了以下功能:
✅ exists(path) - 检查文件或目录是否存在
✅ createDirectory(path) - 创建目录(支持递归创建)
✅ getFileContents(path, options) - 获取文件内容(支持 text/binary/json 格式)
✅ putFileContents(path, content, options) - 上传文件a内容(支持覆盖选项)
✅ getDirectoryContents(path) - 列出目录内容(支持扁平化返回子目录)
✅ deleteFile(path) - 删除文件或目录
✅ moveFile(fromPath, toPath) - 移动/重命名文件
✅ copyFile(fromPath, toPath) - 复制文件
特性
🔐 支持 Basic Authentication
🔄 自动处理路径标准化
📁 支持递归创建目录和扁平化返回子目录
⚠️ 完善的错误处理
下面是你当前 WebDAVClient 库的完整使用说明、示例、注意事项与常见问题排查。文档面向在 Tampermonkey / Userscript 环境里使用该客户端的场景(库实现依赖 GM_xmlhttpRequest)。
WebDAVClient 是一个轻量的 WebDAV 客户端类,封装了常用的 WebDAV 操作:列目录、读取/写入文件、创建目录、删除/移动/复制文件等。它在 userscript 中通过 GM_xmlhttpRequest 发起请求,并模拟 fetch 风格的响应对象(提供 text(), json(), arrayBuffer() 方法)。
两种常见方式:
@require 引入(推荐复用):// @grant GM_xmlhttpRequest
// @connect *
// @require https://update.greasyfork.org/scripts/554239/1686624/WebDAVClient.js?v=1.0.0
注意:如果
WebDAVClient使用GM_xmlhttpRequest,主 userscript 必须声明@grant GM_xmlhttpRequest,否则外部脚本无法访问该 API。 如果想在页面 console 或页面脚本访问该类,需要在外部库末尾globalThis.WebDAVClient = WebDAVClient;(否则类仅在 userscript 沙箱中可见)。
// 假设 WebDAVClient 已可用(通过 @require 或内联定义)
const client = new WebDAVClient({
url: 'https://jike.teracloud.jp/dav/',
username: 'wilsons',
password: 'your-password',
savePath: '/cursor-chat-history', // 可选:仅作记录/业务用途
});
// 列出一级目录(默认)
const list = await client.getDirectoryContents('/cursor-chat-history');
// 列出包括子目录(递归)
const listAll = await client.getDirectoryContents('/cursor-chat-history', { recursive: true });
// 读取文本文件
const txt = await client.getFileContents('/cursor-chat-history/hello.html', { format: 'text' });
// 上传(PUT)文件(覆盖)
await client.putFileContents('/cursor-chat-history/hello.html', 'hello world', { overwrite: true });
// 创建目录(递归)
await client.createDirectory('/cursor-chat-history/sub/dir', { recursive: true });
// 删除
await client.deleteFile('/cursor-chat-history/old.txt');
// 移动 / 复制
await client.moveFile('/from/path.txt', '/to/path.txt', { overwrite: true });
await client.copyFile('/from/path.txt', '/to/copy.txt', { overwrite: false });
所有方法都是
async,遇到 HTTP 错误或网络异常会抛异常(throw)。调用时请try/catch。
new WebDAVClient({ url, username, password, savePath? })
url(必):WebDAV 根 URL,示例:https://server.example/dav/。内部会去掉尾部 / 以便拼接。username, password(必):用于构造 Basic Auth。savePath(可选):仅为业务字段,不影响客户端功能。内部方法,包装 GM_xmlhttpRequest,返回一个模拟 fetch 响应对象(含 ok, status, statusText, text(), json(), arrayBuffer())。通常无需直接调用。
async exists(path) -> boolean
使用 HEAD 请求判断资源是否存在,无法连通或出错返回 false(并在控制台报错)。
创建目录;当 recursive: true 时会逐层 MKCOL。如果目录已存在且服务器返回 405(Method Not Allowed)会视为成功(因为 MKCOL 在已存在时常返回 405)。
async getDirectoryContents(path, { recursive }) -> Array<{ filename, path, type }>
Depth: 1)。recursive: true,函数会对每个子目录递归调用自身并返回扁平数组(目录条目会保留 type: 'directory')。返回项结构:
filename:文件名(已 decodeURIComponent)path:原始 href 字符串(服务器返回)type:'file' | 'directory'库会默认过滤掉 macOS AppleDouble 文件(以 ._ 开头)和 .DS_Store,若需要可修改过滤规则。
读取文件。format 支持:'text'(默认),'binary'(返回 ArrayBuffer),'json'(返回解析后的对象)。请求二进制请用 format: 'binary'(会设置 responseType: 'arraybuffer')。
上传文件(PUT)。
content 可为字符串或二进制(若为二进制,需自己处理 Content-Type)。options.overwrite === false 时,只在文件不存在时写入;否则抛出错误。options.contentType 可指定 Content-Type。删除文件 / 目录(由服务器实现行为决定)。
MOVE,会设置 Destination 为完整 URL(库会调用 _getFullUrl(toPath)),Overwrite 为 T/F。
COPY 操作,参数与 moveFile 相同。
// ==UserScript==
// @name my-webdav-script
// @match *://*/*
// @grant GM_xmlhttpRequest
// @connect *
// @require https://update.greasyfork.org/scripts/554239/1686624/WebDAVClient.js?v=1.0.0
// ==/UserScript==
(async () => {
try {
const client = new WebDAVClient({
url: 'https://jike.teracloud.jp/dav/',
username: 'wilsons',
password: await promptPassword() // 不要把密码硬编码
});
// 列出并打印一级文件
const list = await client.getDirectoryContents('/cursor-chat-history');
console.log('list:', list);
// 递归列出全部
const all = await client.getDirectoryContents('/cursor-chat-history', { recursive: true });
console.log('all files (flat):', all);
// 读文件
const text = await client.getFileContents('/cursor-chat-history/hello.html');
console.log('hello:', text);
// 写文件
await client.putFileContents('/cursor-chat-history/new.txt', 'hello from script', { overwrite: true });
console.log('uploaded');
} catch (err) {
console.error('WebDAV 操作出错:', err);
}
})();
@grant GM_xmlhttpRequest 与合适的 @connect(或 @connect * 用于调试)。若你看到 status: 0 且错误 URL is blacklisted:
<D:href> 可能是绝对路径(/dav/...)或完整 URL;库会用 new URL(href, baseUrl) 进行规范化。getDirectoryContents 默认跳过代表目录自身的 <href> 条目(即 /dav/dir/),所以不会把父目录重复列出。._* 文件._* 和 .DS_Store。如果你想包含这些,请修改过滤逻辑(getDirectoryContents 返回处)。PROPFIND(Depth: 1)。大量文件或深目录会产生许多 HTTP 请求。若服务器允许 Depth: infinity,可考虑一次性请求(需要修改库;兼容性/权限问题多)。prompt() 用户输入或使用受控存储(注意安全)。window(globalThis.WebDAVClient = WebDAVClient),页面脚本与其他扩展可访问该构造函数,避免把敏感实例或凭证也放到全局。Q:为什么我看到 .DS_Store 或 ._xxx?
A:这是 macOS 文件系统的元数据或资源叉文件。库默认会过滤掉;若你想看到,修改过滤规则。
Q:getDirectoryContents 可以返回嵌套树结构吗?
A:当前实现返回扁平数组(目录与文件在同一数组中,type 字段标识目录)。如果你需要嵌套树(每个目录有 children),可以在 getDirectoryContents 内部修改实现。我可以帮你改成嵌套结构(只需修改该函数)。
Q:怎么在页面控制台使用 WebDAVClient?
A:确保外部库在末尾做了 globalThis.WebDAVClient = WebDAVClient;。并且脚本要运行在能访问该全局的上下文(注意 @grant 会导致脚本运行在沙箱,需显式挂到 window)。
Q:服务器返回非标准 XML / 命名空间不同怎么办?
A:库已做命名空间无关查找(getElementsByTagNameNS('*', ...)),能处理常见 D: / lp1: 前缀。如果遇到极端差异,请把 PROPFIND 响应贴上来,我会给出解析调整。
WebDAVClient 放在外部并用 @require 引入(便于复用与维护),并在主脚本中不要硬编码密码。Depth: infinity 或专用同步接口。getDirectoryContents 改为返回嵌套树(children),或把库改成支持 Depth: infinity 的一次性采集、或改进对二进制上传/下载的示例,我只修改最小必要代码并把完整代码输出给你(并标注修改位置)。