最省事方式是用 go-webdav 库配 webdav.handler{allowwrite: true, locksystem: newmemlocksystem()},配合 os.dirfs 和自定义认证中间件,注意客户端兼容性、路径校验与并发安全。

用 go-webdav 启动一个可读写的 WebDAV 服务
直接跑起来最省事的方式是用 go-webdav 这个库,它不依赖外部存储、不强制绑定数据库,适合快速暴露本地目录。但默认配置下它是只读的,想上传必须显式开启写权限。
- 初始化时传入
&webdav.Handler{FileSystem: fs, LockSystem: webdav.NewMemLockSystem(), Options: webdav.Options{AllowWrite: true}} -
fs必须是实现了webdav.FileSystem接口的对象,通常用webdav.NewFileServer包裹的本地路径,比如os.DirFS("/data") - 别漏掉
LockSystem—— 即使只是单机用,缺失会导致 macOS Finder 或 Windows 映射盘报HTTP 500或“无法锁定资源” - 监听端口建议避开 80/443(需要 root),先用
:8080测试,确认能列目录、上传、删除再加反向代理和 TLS
处理 Windows 和 macOS 客户端的兼容性问题
Windows 资源管理器和 macOS Finder 对 WebDAV 的实现很“挑”,不是返回 200 就算通。它们会发一堆 PROPFIND、OPTIONS、LOCK 请求,任一环节响应格式或状态码不对就会卡在“正在连接”或弹出认证框后无限转圈。
- 确保
OPTIONS响应头包含Dav: 1,2和Allow: GET,HEAD,PUT,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK - PROPFIND 必须返回完整 XML,且
<href></href>值要以/开头并和请求路径一致(比如服务跑在/dav下,href 就得是/dav/filename,不能是/filename) - macOS 会发带
If-None-Match: *的 HEAD 请求来探测文件是否存在,你的HEAD处理逻辑得支持这个 header,否则显示“文件已损坏” - Windows 在上传大文件时可能分块发 PUT,不要假设一次
Request.Body就读完全部内容 —— 必须用io.Copy流式写入磁盘
加基础认证但别碰 http.BasicAuth 中间件
http.BasicAuth 是 HTTP Basic 认证的简易封装,但它会把所有未认证请求直接 401,而 WebDAV 客户端(尤其是 Windows)在初始 OPTIONS 请求时并不带 Auth header,直接 401 会导致握手失败。
- 自己写中间件,在
OPTIONS和PROPFIND(根路径)等预检请求上跳过鉴权 - 认证信息从
req.Header.Get("Authorization")提取,base64 解码后比对,别用http.Request.BasicAuth()—— 它会吃掉错误格式的 header 导致空用户名 - 密码别硬编码,至少从环境变量读,比如
os.Getenv("WEBDAV_USER")和os.Getenv("WEBDAV_PASS") - 如果后续要上 HTTPS,记得关掉
http.Redirect自动跳转,WebDAV 客户端不认 301/302
文件系统权限与并发写入风险
Go 的 os.DirFS 是只读包装,真要写必须用自定义 FileSystem 实现。但自己实现 Mkdir、RemoveAll 等方法时,容易忽略操作系统级权限和并发冲突。
立即学习“go语言免费学习笔记(深入)”;
- 确保运行 Go 程序的用户对
/data目录有rwx权限,Linux 下常见问题是 SELinux 或 systemd 沙箱限制(比如ProtectHome=true) -
Put方法里别直接os.WriteFile,要用os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_TRUNC)配合defer f.Close(),否则并发上传同名文件会丢数据 - 删除操作(
Delete)务必检查路径是否在允许范围内,防止客户端传../etc/passwd—— 用filepath.Clean()+strings.HasPrefix()做白名单校验 - WebDAV 没有事务概念,
COPY或MOVE失败时可能留下半截文件,建议在Put前先写临时文件,成功后再os.Rename










