上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖
楼主: wilsons - 

简单webdav脚本库

[复制链接]
  • TA的每日心情
    奋斗
    5 天前
  • 签到天数: 69 天

    [LV.6]常住居民II

    20

    主题

    87

    回帖

    169

    积分

    荣誉开发者

    积分
    169

    荣誉开发者油中2周年油中3周年新人报道挑战者 lv2喜迎中秋

    发表于 昨天 19:55 | 显示全部楼层 | 阅读模式

    如何使用? 库问题反馈 给库评分 查看代码

    缘起

    现在webdav应该经常被用到,尤其涉及到数据备和份同步时。

    开发 Cursor Chat 时,为了支持保存会话历史,于是让gpt5写了这个库,一开始想着在网上找开源代码,但多是fetch获取,存在跨域问题,而让ai调整后也不理想,于是让ai自己写了一个简单的webdav库。简单测试下来感觉还可以。就顺便发到了脚本库,或许还有其他人会用,如果有问题可以留言反馈。

    这里也顺便推荐两个webdav平台:

    1. koofr ://app.koofr.net/ 10G免费空间
    2. InfiniCLOUD https://jike.teracloud.jp/ 【推荐】服务器在日本,速度快,稳定,默认20G空间。(如果输入我的推荐码:QEU7Z 还可以额外增加5G永久空间(方法:点击顶部导航进入 My Page页面,找到找到 Enter Friends Referral Code输入即可))

    帮助文档

    主要功能

    这个 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 使用文档(中文版 by chatgpt5)

    下面是你当前 WebDAVClient 库的完整使用说明、示例、注意事项与常见问题排查。文档面向在 Tampermonkey / Userscript 环境里使用该客户端的场景(库实现依赖 GM_xmlhttpRequest)。


    概览

    WebDAVClient 是一个轻量的 WebDAV 客户端类,封装了常用的 WebDAV 操作:列目录、读取/写入文件、创建目录、删除/移动/复制文件等。它在 userscript 中通过 GM_xmlhttpRequest 发起请求,并模拟 fetch 风格的响应对象(提供 text(), json(), arrayBuffer() 方法)。


    引入 / 安装(示例)

    两种常见方式:

    1. 把类放到同一脚本中(直接定义 class)——你已有的做法。
    2. 把类放外部,并用 @require 引入(推荐复用):
    // @grant       GM_xmlhttpRequest
    // @connect     *
    // @require     https://scriptcat.org/lib/4521/1.0.0/WebDAVClient.js?sha384-H3wUQ1kAa17QQ2r8YgdyAyJbQRuk9zm9e0dejIiiHAt2frfUwsSxOLOqsmOCiYIV

    注意:如果 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 });

    API 参考(方法与行为)

    所有方法都是 async,遇到 HTTP 错误或网络异常会抛异常(throw)。调用时请 try/catch

    构造函数

    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)),OverwriteT/F

    copyFile(fromPath, toPath, options = { overwrite: false })

    COPY 操作,参数与 moveFile 相同。


    常见使用场景示例(更完整)

    userscript 头部示例(@require 引入)

    // ==UserScript==
    // @name         my-webdav-script
    // @match        *://*/*
    // @grant        GM_xmlhttpRequest
    // @connect      *
    // @require     https://scriptcat.org/lib/4521/1.0.0/WebDAVClient.js?sha384-H3wUQ1kAa17QQ2r8YgdyAyJbQRuk9zm9e0dejIiiHAt2frfUwsSxOLOqsmOCiYIV
    // ==/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);
      }
    })();

    排查与注意事项

    1. Tampermonkey 权限 / 黑名单问题

    • 必须在脚本头加上 @grant GM_xmlhttpRequest 与合适的 @connect(或 @connect * 用于调试)。
    • 若你看到 status: 0 且错误 URL is blacklisted

      • 检查 Tampermonkey 仪表盘脚本设置的 XHR 黑名单/白名单;
      • 如果首次弹窗被拒绝或被记住拒绝,需在 Tampermonkey 设置中清除或重置 XHR 权限;
      • 也检查浏览器或其它扩展(uBlock/NoScript)是否阻止了请求。

    2. 路径与 href 格式

    • 服务器返回 <D:href> 可能是绝对路径(/dav/...)或完整 URL;库会用 new URL(href, baseUrl) 进行规范化。
    • 注意 getDirectoryContents 默认跳过代表目录自身的 <href> 条目(即 /dav/dir/),所以不会把父目录重复列出。

    3. macOS 产生的 ._* 文件

    • 这些是 AppleDouble(资源叉)文件,会出现在目录中。库默认过滤 ._*.DS_Store。如果你想包含这些,请修改过滤逻辑(getDirectoryContents 返回处)。

    4. 递归与性能

    • 递归会对每个子目录额外发起一次 PROPFIND(Depth: 1)。大量文件或深目录会产生许多 HTTP 请求。若服务器允许 Depth: infinity,可考虑一次性请求(需要修改库;兼容性/权限问题多)。
    • 若需要高性能同步,考虑服务器端支持或分页、增量 sync。

    5. 安全提示

    • 不要把明文密码硬编码发布到公开仓库或脚本头。可在运行时 prompt() 用户输入或使用受控存储(注意安全)。
    • 若将类暴露到 windowglobalThis.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 的一次性采集、或改进对二进制上传/下载的示例,我只修改最小必要代码并把完整代码输出给你(并标注修改位置)。
    已有1人评分好评 油猫币 理由
    王一之 + 1 + 4 赞一个!

    查看全部评分 总评分:好评 +1  油猫币 +4 

    无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人。

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表