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 存储桶中的文件。主要功能包括:
- 身份验证: 使用 HTTP Basic Auth 验证用户身份,用户名和密码从环境变量读取。
- 文件操作: 支持 GET (下载), PUT (上传), DELETE (删除) 文件。
- 目录浏览 (PROPFIND): 支持使用 PROPFIND 命令获取文件列表和属性。
- 禁用目录创建 (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 | <已省略> |
如何使用:
- 在 Cloudflare 控制面板中创建 R2 存储桶: 如果您还没有这样做,请创建一个新的 R2 存储桶。
- 创建 Cloudflare Worker: 在 Cloudflare 控制面板中创建一个新的 Worker。
- 复制并粘贴代码: 将上面的代码复制粘贴到您的 Worker 代码编辑器中。
- 绑定 R2 存储桶:
- 在 Worker 设置中找到 “绑定” 部分。
- 添加一个名为
MY_R2_BUCKET
的绑定。 - 选择 “R2 Bucket” 作为变量类型。
- 选择您在第一步创建的 R2 存储桶。
- 添加验证信息:
- 在 Worker 设置中找到 “变量” 部分。
- 添加两个名为环境变量
WEBDAV_USERNAME
和WEBDAV_PASSWORD
的文本类型。 - 分别存储用户名和密码。
- 部署 Worker: 保存并部署您的 Worker。
- 测试: 使用支持 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 存储桶)。
绑定如何工作:
- 在 Cloudflare 控制面板中定义绑定:
- 在你的 Cloudflare Worker 的设置页面中,你会找到一个 “变量” 或 “绑定(Bindings)” 的部分。
- 在这里,你可以创建新的绑定。对于 R2 存储桶,你需要创建一个 “R2 Bucket” 类型的绑定。
- 在创建绑定时,你会给它一个名称(例如
MY_R2_BUCKET
,正如代码示例中使用的那样),并选择你要绑定的 R2 存储桶。
- 在 Worker 代码中使用绑定的名称:
- 在你的 Worker 代码中,你可以通过绑定的名称来访问绑定的资源。
- 例如,在上面的示例代码中,我们使用
env.MY_R2_BUCKET
来访问之前在控制面板中绑定的 R2 存储桶。
代码中为什么没有存储桶的名称:
- 安全性: 这种方式可以避免在代码中暴露敏感信息(如存储桶名称和密钥)。
- 灵活性: 你可以轻松地切换 Worker 使用的 R2 存储桶,而无需修改代码。你只需要在控制面板中修改绑定即可。
- 模块化: 你可以将 Worker 代码与特定的存储桶解耦,使得代码更加模块化和可维护。
总结:
- 不是在代码中写存储桶名称: 你不在 Worker 代码中直接硬编码 R2 存储桶的名称。
- 使用绑定的名称: 你在代码中使用在 Cloudflare 控制面板中定义的绑定名称(例如
MY_R2_BUCKET
)。 - 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
属性:getcontentlength
是PROPFIND
方法返回的一个属性,用于指示文件的大小(字节数)。- 默认代码的不足: 在我们之前的代码中,
createPropfindResponse
函数返回的getcontentlength
值是硬编码为0
的,并没有使用 R2 存储桶的实际文件大小。 这就是为什么文件浏览器无法获取到正确的文件大小信息。
解决方案:
你需要修改 createPropfindResponse
函数,使其从 R2 存储桶获取实际的文件大小信息,并返回到 PROPFIND
响应中。
修改后的代码:
1 | <已省略> |
修改说明:
- 修改
createPropfindResponse
函数:- 增加了
env
参数。 - 如果不是目录或者根目录,就调用
env.MY_R2_BUCKET.get(path)
获取对象。 - 从 R2 对象中获取
size
属性,作为文件大小。 - 如果 R2 对象不存在,设置
contentLength
为 0。
- 增加了
- 修改 handlePropfind函数:
- 将
env
传递给createPropfindResponse
函数。 - 使用了 async/await 确保顺序执行
- 将
如何使用:
- 替换代码: 将修改后的
createPropfindResponse
函数替换到你的 Worker 代码中。 - 部署 Worker: 部署修改后的 Worker 代码。
- 测试: 使用你的文件浏览器重新连接到 WebDAV 服务器。 现在应该可以正确地显示文件大小信息了。
总结:
- 重要性: 准确的文件大小信息对于文件浏览器来说非常重要,这个修改可以提升你的 WebDAV 服务器的易用性。
getcontentlength
属性:getcontentlength
属性必须从 R2 存储桶获取,而不是硬编码。- WebDAV 协议: 理解
PROPFIND
方法和相关属性是构建完整的 WebDAV 服务器的关键。
通过这个修改,你的 WebDAV 服务器现在应该能够正确地提供文件大小信息,并且你的文件浏览器也应该能够正确地显示这些信息了。
如果你还有其他问题,请随时提出。 期待你的反馈!