Greasy Fork is available in English.
webdav简单客户端,支持创建文件,列目录,读取文件,写入文件,删除文件,移动/复制文件,判断文件是否存在等
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/554239/1686622/WebDAVClient.js
主要功能
这个 WebDAV 客户端实现了以下功能:
✅ exists(path) - 检查文件或目录是否存在
✅ createDirectory(path) - 创建目录(支持递归创建)
✅ getFileContents(path, options) - 获取文件内容(支持 text/binary/json 格式)
✅ putFileContents(path, content, options) - 上传文件内容(支持覆盖选项)
✅ getDirectoryContents(path) - 列出目录内容(支持扁平化返回子目录)
✅ deleteFile(path) - 删除文件或目录
✅ moveFile(fromPath, toPath) - 移动/重命名文件
✅ copyFile(fromPath, toPath) - 复制文件
特性
🔐 支持 Basic Authentication
🔄 自动处理路径标准化
📁 支持递归创建目录和扁平化返回子目录
⚠️ 完善的错误处理
---
# WebDAVClient 使用文档(中文版 by chatgpt5)
下面是你当前 `WebDAVClient` 库的完整使用说明、示例、注意事项与常见问题排查。文档面向在 Tampermonkey / Userscript 环境里使用该客户端的场景(库实现依赖 `GM_xmlhttpRequest`)。
---
# 概览
`WebDAVClient` 是一个轻量的 WebDAV 客户端类,封装了常用的 WebDAV 操作:列目录、读取/写入文件、创建目录、删除/移动/复制文件等。它在 userscript 中通过 `GM_xmlhttpRequest` 发起请求,并模拟 `fetch` 风格的响应对象(提供 `text()`, `json()`, `arrayBuffer()` 方法)。
---
# 引入 / 安装(Tampermonkey 示例)
两种常见方式:
1. **把类放到同一脚本中**(直接定义 class)——你已有的做法。
2. **把类放外部,并用 `@require` 引入**(推荐复用):
```js
// @grant GM_xmlhttpRequest
// @connect *
// @require https://cdn.jsdelivr.net/gh/yourname/[email protected]/webdav-client.js
```
> 注意:如果 `WebDAVClient` 使用 `GM_xmlhttpRequest`,主 userscript 必须声明 `@grant GM_xmlhttpRequest`,否则外部脚本无法访问该 API。
> 如果想在页面 console 或页面脚本访问该类,需要在外部库末尾 `globalThis.WebDAVClient = WebDAVClient;`(否则类仅在 userscript 沙箱中可见)。
---
# 快速开始(实例化与基本用法)
```js
// 假设 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 });
```
---
# API 参考(方法与行为)
> 所有方法都是 `async`,遇到 HTTP 错误或网络异常会抛异常(`throw`)。调用时请 `try/catch`。
## 构造函数
```js
new WebDAVClient({ url, username, password, savePath? })
```
* `url`(必):WebDAV 根 URL,示例:`https://server.example/dav/`。内部会去掉尾部 `/` 以便拼接。
* `username`, `password`(必):用于构造 Basic Auth。
* `savePath`(可选):仅为业务字段,不影响客户端功能。
## _request(method, path, options)
内部方法,包装 `GM_xmlhttpRequest`,返回一个模拟 `fetch` 响应对象(含 `ok`, `status`, `statusText`, `text()`, `json()`, `arrayBuffer()`)。通常无需直接调用。
## exists(path)
`async exists(path) -> boolean`
使用 `HEAD` 请求判断资源是否存在,无法连通或出错返回 `false`(并在控制台报错)。
## createDirectory(path, options = { recursive: true })
创建目录;当 `recursive: true` 时会逐层 `MKCOL`。如果目录已存在且服务器返回 405(Method Not Allowed)会视为成功(因为 MKCOL 在已存在时常返回 405)。
## getDirectoryContents(path, options = { recursive: false })
`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`,若需要可修改过滤规则。
## getFileContents(path, options = { format: 'text' })
读取文件。`format` 支持:`'text'`(默认),`'binary'`(返回 `ArrayBuffer`),`'json'`(返回解析后的对象)。请求二进制请用 `format: 'binary'`(会设置 `responseType: 'arraybuffer'`)。
## putFileContents(path, content, options = { overwrite: true })
上传文件(PUT)。
* `content` 可为字符串或二进制(若为二进制,需自己处理 `Content-Type`)。
* `options.overwrite === false` 时,只在文件不存在时写入;否则抛出错误。
* `options.contentType` 可指定 `Content-Type`。
## deleteFile(path)
删除文件 / 目录(由服务器实现行为决定)。
## moveFile(fromPath, toPath, options = { overwrite: false })
`MOVE`,会设置 `Destination` 为完整 URL(库会调用 `_getFullUrl(toPath)`),`Overwrite` 为 `T`/`F`。
## copyFile(fromPath, toPath, options = { overwrite: false })
`COPY` 操作,参数与 `moveFile` 相同。
---
# 常见使用场景示例(更完整)
### userscript 头部示例(@require 引入)
```js
// ==UserScript==
// @name my-webdav-script
// @match *://*/*
// @grant GM_xmlhttpRequest
// @connect *
// @require https://cdn.jsdelivr.net/gh/yourname/[email protected]/webdav-client.js
// ==/UserScript==
```
### 实际操作示例(含错误处理)
```js
(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);
}
})();
```
---
# 排查与注意事项
## 1. Tampermonkey 权限 / 黑名单问题
* 必须在脚本头加上 `@grant GM_xmlhttpRequest` 与合适的 `@connect`(或 `@connect *` 用于调试)。
* 若你看到 `status: 0` 且错误 `URL is blacklisted`:
* 检查 Tampermonkey 仪表盘脚本设置的 XHR 黑名单/白名单;
* 如果首次弹窗被拒绝或被记住拒绝,需在 Tampermonkey 设置中清除或重置 XHR 权限;
* 也检查浏览器或其它扩展(uBlock/NoScript)是否阻止了请求。
## 2. 路径与 href 格式
* 服务器返回 `` 可能是绝对路径(`/dav/...`)或完整 URL;库会用 `new URL(href, baseUrl)` 进行规范化。
* 注意 `getDirectoryContents` 默认跳过代表目录自身的 `` 条目(即 `/dav/dir/`),所以不会把父目录重复列出。
## 3. macOS 产生的 `._*` 文件
* 这些是 AppleDouble(资源叉)文件,会出现在目录中。库默认过滤 `._*` 和 `.DS_Store`。如果你想包含这些,请修改过滤逻辑(getDirectoryContents 返回处)。
## 4. 递归与性能
* 递归会对每个子目录额外发起一次 `PROPFIND`(Depth: 1)。大量文件或深目录会产生许多 HTTP 请求。若服务器允许 `Depth: infinity`,可考虑一次性请求(需要修改库;兼容性/权限问题多)。
* 若需要高性能同步,考虑服务器端支持或分页、增量 sync。
## 5. 安全提示
* **不要把明文密码硬编码发布到公开仓库或脚本头**。可在运行时 `prompt()` 用户输入或使用受控存储(注意安全)。
* 若将类暴露到 `window`(`globalThis.WebDAVClient = WebDAVClient`),页面脚本与其他扩展可访问该构造函数,避免把敏感实例或凭证也放到全局。
---
# 常见问题(FAQ)
* **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` 响应贴上来,我会给出解析调整。
---
# 结语 / 推荐实践
* 对于 userscript:尽量把 `WebDAVClient` 放在外部并用 `@require` 引入(便于复用与维护),并在主脚本中**不要**硬编码密码。
* 在大目录或同步场景下,优先考虑服务器端支持更高效的 `Depth: infinity` 或专用同步接口。
* 若你希望我把 `getDirectoryContents` 改为返回嵌套树(`children`),或把库改成支持 `Depth: infinity` 的一次性采集、或改进对二进制上传/下载的示例,我只修改最小必要代码并把完整代码输出给你(并标注修改位置)。