抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

00 需求

考虑到Cloudflare提供了R2和Workers提供这两项绝佳的免费服务,我们可以搭建一个持久的劣化版Webdav服务器来存放我们Floccus书签等等文件。
我使用Gemini帮助我创建了一段js代码,工作在Cloudflare Workers上。
请注意,这个方案的特点:

  • 基于R2的存储桶对文件夹的处理较为特殊。建议只在根目录保存文件。
  • 使用环境变量来存储用户名与密码,这样可以最大程度地确保安全性。

01 使用说明

我们只需要部署在Cloudflare Workers上就好。
请注意,在使用前请确保:

  • 你已经搭建了一个 R2存储桶。
  • 添加环境变量 WEBDAV_USERNAME 和 WEBDAV_PASSWORD存储用户名和密码。
  • 添加绑定MY_R2_BUCKET来选择你的存储桶。

02 LLM 解析

这段代码实现了一个基本的 WebDAV 服务器,使用 Cloudflare R2 作为存储后端。
整体架构和功能
这段代码的主要目标是创建一个简单的 WebDAV 服务,允许用户通过 WebDAV 客户端(如 Floccus 书签同步工具)访问存储在 Cloudflare R2 存储桶中的文件。主要功能包括:

  1. 身份验证: 使用 HTTP Basic Auth 验证用户身份,用户名和密码从环境变量读取。
  2. 文件操作: 支持 GET (下载), PUT (上传), DELETE (删除) 文件。
  3. 目录浏览 (PROPFIND): 支持使用 PROPFIND 命令获取文件列表和属性。
  4. 禁用目录创建 (MKCOL): 由于 R2 的特性,禁止创建文件夹。

重要特性和改进

  • R2 集成: 使用 Cloudflare R2 作为后端存储,充分利用 R2 的存储能力和速度。
  • 时间戳修复: 修复了之前版本时间戳固定的问题,正确使用了 R2 对象的 uploaded 属性。
  • 禁止目录操作: 考虑到 R2 的特性,禁止创建目录操作。
  • 详细日志: 在关键操作中加入了 console.log 方便调试。
  • 路径处理: 对路径进行了规范化,移除了开头和结尾的 /,同时支持 URL 解码。

总结
这段代码是一个简洁而功能完整的 WebDAV 服务器实现,充分利用了 Cloudflare Workers 和 R2 的能力。代码结构清晰,易于理解,可作为进一步开发和扩展的基础。它特别适合需要一个简单、安全、可靠的 WebDAV 解决方案,并利用 Cloudflare 生态系统的场景。请确保在部署之前,仔细评估上述的潜在改进和注意事项。

03 代码

传送门:code.js
请把这个文件另存到本地,然后复制其中的所有内容,粘贴到Workers的编辑器中。

04 与LLM的Q&A(心得)

Q1

我已经创建了一个cloudflare 存储桶,需要创建一个webdav服务器。现在给出对应的cloudflare workers的代码;webdav的登录用户名和密码均为admin。

好的,这是一个使用 Cloudflare Workers 和 Cloudflare R2 存储桶实现 WebDAV 服务器的基本代码示例。请注意,为了安全起见,强烈建议您不要在生产环境中使用硬编码的 “admin” 用户名和密码。您应该使用更强大的身份验证机制,例如环境变量或密钥管理服务。

代码示例:

1
<已省略>

如何使用:

  1. 在 Cloudflare 控制面板中创建 R2 存储桶: 如果您还没有这样做,请创建一个新的 R2 存储桶。
  2. 创建 Cloudflare Worker: 在 Cloudflare 控制面板中创建一个新的 Worker。
  3. 复制并粘贴代码: 将上面的代码复制粘贴到您的 Worker 代码编辑器中。
  4. 绑定 R2 存储桶:
    • 在 Worker 设置中找到 “绑定” 部分。
    • 添加一个名为 MY_R2_BUCKET 的绑定。
    • 选择 “R2 Bucket” 作为变量类型。
    • 选择您在第一步创建的 R2 存储桶。
  5. 添加验证信息:
    • 在 Worker 设置中找到 “变量” 部分。
    • 添加两个名为环境变量 WEBDAV_USERNAME 和 WEBDAV_PASSWORD的文本类型。
    • 分别存储用户名和密码。
  6. 部署 Worker: 保存并部署您的 Worker。
  7. 测试: 使用支持 WebDAV 的客户端(例如 Cyberduck、Transmit 或 rclone)连接到您的 Worker URL。

重要注意事项:

  • 安全性: 正如上面提到的,强烈建议您不要在生产环境中使用硬编码的 “admin” 用户名和密码。考虑使用环境变量、密钥管理或其他更安全的身份验证方法。
  • 错误处理: 上面的代码是一个基本的示例,您应该添加更完善的错误处理机制。
  • 性能: 对于大文件,您可能需要考虑优化数据传输方式,例如使用分块上传。
  • R2 API: 您可能需要查看 Cloudflare R2 API 文档,以了解更多高级功能。

功能说明:

  • 基本认证: 使用 admin:admin 作为用户名和密码进行身份验证。
  • GET: 从 R2 存储桶下载文件。
  • PUT: 上传文件到 R2 存储桶。
  • DELETE: 从 R2 存储桶删除文件。
  • PROPFIND: 获取文件/目录的元数据,这是 WebDAV 客户端用于浏览目录的必要操作。
  • MKCOL: 创建目录。

改进方向:

  • 更安全的身份验证: 使用环境变量、密钥管理、JWT 或 OAuth 2.0。
  • 用户管理: 为不同用户分配不同的访问权限。
  • 自定义错误消息: 提供更详细的错误信息。
  • 性能优化: 实现分块上传/下载。
  • WebDAV 功能: 支持更多的 WebDAV 命令,如 COPY, MOVE 等。
  • 支持大文件: 支持大文件上传/下载。
  • HTTPS: 确保使用 HTTPS 连接。

这个示例应该足以让您开始使用 Cloudflare Worker 构建简单的 WebDAV 服务器。请记住,为了安全和可靠,在生产环境中使用之前,应该进行进一步的改进和测试。

希望这个示例对您有所帮助!如果您有任何其他问题,请随时提问。

Q2

这个代码不需要提供存储桶的信息吗?

你问得很好!这是个非常关键的问题,也是我之前解释中有所遗漏的地方,你需要提供存储桶的信息,但不是直接在代码中硬编码,而是通过 Cloudflare Workers 的绑定(Binding)机制来实现。

详细解释:

在 Cloudflare Workers 中,我们不会直接在代码中写死 R2 存储桶的名称或访问密钥。相反,我们使用 “绑定(Bindings)” 来安全地引用外部资源(例如,R2 存储桶)。

绑定如何工作:

  1. 在 Cloudflare 控制面板中定义绑定:
    • 在你的 Cloudflare Worker 的设置页面中,你会找到一个 “变量”“绑定(Bindings)” 的部分。
    • 在这里,你可以创建新的绑定。对于 R2 存储桶,你需要创建一个 “R2 Bucket” 类型的绑定。
    • 在创建绑定时,你会给它一个名称(例如 MY_R2_BUCKET,正如代码示例中使用的那样),并选择你要绑定的 R2 存储桶。
  2. 在 Worker 代码中使用绑定的名称:
    • 在你的 Worker 代码中,你可以通过绑定的名称来访问绑定的资源。
    • 例如,在上面的示例代码中,我们使用 env.MY_R2_BUCKET 来访问之前在控制面板中绑定的 R2 存储桶。

代码中为什么没有存储桶的名称:

  • 安全性: 这种方式可以避免在代码中暴露敏感信息(如存储桶名称和密钥)。
  • 灵活性: 你可以轻松地切换 Worker 使用的 R2 存储桶,而无需修改代码。你只需要在控制面板中修改绑定即可。
  • 模块化: 你可以将 Worker 代码与特定的存储桶解耦,使得代码更加模块化和可维护。

总结:

  1. 不是在代码中写存储桶名称: 你不在 Worker 代码中直接硬编码 R2 存储桶的名称。
  2. 使用绑定的名称: 你在代码中使用在 Cloudflare 控制面板中定义的绑定名称(例如 MY_R2_BUCKET)。
  3. Cloudflare 处理绑定: Cloudflare 会在运行时将绑定名称映射到你指定的 R2 存储桶。

代码示例中的 env 对象:

  • env 对象是一个特殊的 Cloudflare Worker 上下文对象,它包含了绑定的资源、环境变量等等。
  • 通过 env.MY_R2_BUCKET 你可以访问到你绑定的 R2 存储桶实例,并调用它的方法,如 get()put()delete()

所以,你不需要在代码中直接提供存储桶的信息,而是通过在 Cloudflare 控制面板中定义绑定,并在代码中使用绑定的名称来引用存储桶。

Q3

我注意到,如果通过浏览器访问会导向欢迎页面,而通过文件浏览器访问会导向文件目录。

这正是这段代码的预期行为,也是 WebDAV 服务的一个常见特性,因为 WebDAV 协议的工作方式与浏览器访问不同。

代码通过检查请求的 Accept 头部来判断请求来源,如果 Accept 头部包含 text/html,则认为请求来自浏览器,返回欢迎页面;否则,则假定请求来自 WebDAV 客户端,返回文件目录 XML 数据。

Q4

我注意到使用通常的文件浏览器无法获取到文件的大小信息。(已修正)

你观察得非常到位!这确实是一个常见的现象,并且涉及到 WebDAV 协议中 PROPFIND 方法返回的属性。 文件浏览器(如 Windows 文件资源管理器、macOS Finder 或 Linux 中的 Nautilus)依赖于 PROPFIND 方法返回的 getcontentlength 属性来显示文件大小信息。

问题核心:

  • PROPFIND 方法: WebDAV 客户端使用 PROPFIND 方法来获取文件和目录的元数据,包括文件大小、修改时间、创建时间等。
  • getcontentlength 属性: getcontentlengthPROPFIND 方法返回的一个属性,用于指示文件的大小(字节数)。
  • 默认代码的不足: 在我们之前的代码中,createPropfindResponse 函数返回的 getcontentlength 值是硬编码为 0 的,并没有使用 R2 存储桶的实际文件大小。 这就是为什么文件浏览器无法获取到正确的文件大小信息。

解决方案:
你需要修改 createPropfindResponse 函数,使其从 R2 存储桶获取实际的文件大小信息,并返回到 PROPFIND 响应中。

修改后的代码:

1
<已省略>

修改说明:

  1. 修改 createPropfindResponse 函数:
    • 增加了 env 参数。
    • 如果不是目录或者根目录,就调用 env.MY_R2_BUCKET.get(path) 获取对象。
    • 从 R2 对象中获取 size 属性,作为文件大小。
    • 如果 R2 对象不存在,设置 contentLength 为 0。
  2. 修改 handlePropfind函数:
    • env 传递给 createPropfindResponse 函数。
    • 使用了 async/await 确保顺序执行

如何使用:

  1. 替换代码: 将修改后的 createPropfindResponse 函数替换到你的 Worker 代码中。
  2. 部署 Worker: 部署修改后的 Worker 代码。
  3. 测试: 使用你的文件浏览器重新连接到 WebDAV 服务器。 现在应该可以正确地显示文件大小信息了。

总结:

  • 重要性: 准确的文件大小信息对于文件浏览器来说非常重要,这个修改可以提升你的 WebDAV 服务器的易用性。
  • getcontentlength 属性: getcontentlength 属性必须从 R2 存储桶获取,而不是硬编码。
  • WebDAV 协议: 理解 PROPFIND 方法和相关属性是构建完整的 WebDAV 服务器的关键。

通过这个修改,你的 WebDAV 服务器现在应该能够正确地提供文件大小信息,并且你的文件浏览器也应该能够正确地显示这些信息了。

如果你还有其他问题,请随时提出。 期待你的反馈!

评论