前言

继前两篇文章,介绍了两种搭建WebDav的方法:

  • Apache2后端,Nginx反向代理

  • Nginx直接搭建WebDav

这两种方法都存在一个很大的问题,Windows磁盘映射会出现各种错误,而且还不是单一的错误,这两天操碎了心,国内国外文章全部扒了一遍又一遍,所幸最终还是可以解决的。

问题:

  1. 无法创建、上传文件(夹)

  2. 提示:目录内已经存在相同的文件,是否覆盖

  3. 遗留一个0KB 的空白文件

  4. 另一个程序已经锁定文件的一部分,无法访问

问题的核心是 Windows 自带的 WebDAV 客户端(Microsoft-WebDAV-MiniRedir)与服务器的 WebDAV 模块之间的兼容性问题

这个问题不单纯是Nginx或者Apache2,其实很多第三方应用也是一样,比如我尝试过的Openlist,Hfs,它们可能一些小文件上传下载没问题,但是由于我经常要传输蓝光电影,文件大点也是一样的错误。

根源的Windows磁盘映射在搞事情,我们只能顺着它的心意。

顺带说一句,MacOS也不比它好,测试下来半斤八两。

重点

基于前两篇文章已经配置成功并能正常访问,我们只谈一下后续的步骤。

之前正常访问的配置文件:(抛弃Apache2,只用Nginx)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# /etc/nginx/site-enable/wedav.conf
server {

listen 80;
listen [::]:80;

server_name _;

location / {
root /path/to/your/folder;
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
dav_access group:rw all:r;
autoindex on;
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/webdav.htpasswd;
client_max_body_size 0;
create_full_put_path on;
}

}

此时,我们已经安装了 libnginx-mod-http-dav-ext 模块。

继续安装一个 http-headers-more-filter 模块:

1
apt install libnginx-mod-http-headers-more-filter

添加在 /etc/nginx/nginx.conf 的前端:

1
2
load_module modules/ngx_http_dav_ext_module.so; # 之前安装的
load_module modules/ngx_http_headers_more_filter_module.so; # 这次安装的

并且在 nginx.confhttp模块中添加一些配置参数:

1
2
3
4
5
6
7
8
9
10
11
http {
dav_ext_lock_zone zone=foo:10m; # 扩展锁定功能的内存区域
client_max_body_size 0;
create_full_put_path on;
min_delete_depth 0;
charset utf-8;
source_charset utf-8;

# 其他参数

}

然后编辑 /etc/nginx/site-enable/webdav.conf ,这部分仔细一些,一步步慢慢来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# /etc/nginx/site-enable/wedav.conf
server {
listen 80;
listen [::]:80;

server_name _;

# 定义变量
set $destination $http_destination;
set $new_path "";
set $webdav_root "/path/to/your/folder"; # 填写webdav路径
set $checkPropfind "";

#这个location块照抄就行,基本没有要改的东西
location / {
root $webdav_root;
open_file_cache off;
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/webdav.htpasswd;

dav_ext_lock zone=foo;
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;

autoindex on;
autoindex_exact_size on;
autoindex_localtime on;

error_page 599 = @propfind_handler;
error_page 598 = @delete_handler;
error_page 597 = @copy_move_handler;
error_page 596 = @propfind_withdepth_handler;

if ($request_method != OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'OPTIONS, GET, HEAD, POST, PUT, MKCOL, MOVE, COPY, DELETE, PROPFIND, PROPPATCH, LOCK, UNLOCK' always;
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Accept-Charset,X-Accept,origin,accept,if-match,destination,overwrite' always;
add_header 'Access-Control-Expose-Headers' 'ETag' always;
add_header 'Access-Control-Max-Age' 1728000 always;
}

if ($request_method = OPTIONS) {
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'OPTIONS, GET, HEAD, POST, PUT, MKCOL, MOVE, COPY, DELETE, PROPFIND, PROPPATCH, LOCK, UNLOCK';
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Accept-Charset,X-Accept,origin,accept,if-match,destination,overwrite';
add_header 'Access-Control-Expose-Headers' 'ETag';
add_header 'Access-Control-Max-Age' 1728000;
add_header Allow 'OPTIONS, GET, HEAD, POST, PUT, MKCOL, MOVE, COPY, DELETE, PROPFIND, PROPPATCH, LOCK, UNLOCK';
add_header DAV '1, 2';
return 200;
}

if ($request_method = PROPFIND) {
set $checkPropfind "propfind";
}

if ($http_depth = 0) {
set $checkPropfind "${checkPropfind}+withDepth";
}

if ($http_depth = 1) {
set $checkPropfind "${checkPropfind}+withDepth";
}

if ($checkPropfind = "propfind") {
return 599;
}

if ($checkPropfind = "propfind+withDepth") {
return 596;
}

if ($request_method = PROPPATCH) {
add_header Content-Type 'text/xml';
return 207 '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:status>HTTP/1.1 200 OK</a:status></a:propstat></a:response></a:multistatus>';
}

if ($request_method = MKCOL) {
rewrite ^(?<captured_path>.*[^/])$ $captured_path/ break;
}
if ($request_method = DELETE) {
return 598;
}
if ($request_method = COPY) {
return 597;
}
if ($request_method = MOVE) {
return 597;
}
}

# 这几个也不用修改
location ~ \.(_.*|DS_Store|Spotlight-V100|TemporaryItems|Trashes|hidden|localized)$ {
access_log off;
error_log off;
if ($request_method = PUT) {
return 403;
}
return 404;
}

location ~ \.metadata_never_index$ {
return 200 "Don't index this drive, Finder!";
}

location @propfind_handler {
internal;
open_file_cache off;
if (!-e $webdav_root/$uri) {
return 404;
}
root $webdav_root;
dav_ext_methods PROPFIND;
}

# 下面几个location把前两行的身份认证统一一下

location @propfind_withdepth_handler {
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/webdav.htpasswd;
internal;
open_file_cache off;

if (!-e $webdav_root/$uri) {
return 404;
}
root $webdav_root;
dav_ext_methods PROPFIND;
}

location @delete_handler {
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/webdav.htpasswd;
internal;
open_file_cache off;

if ($destination ~ ^https?://(?<captured_path>.*)$) {
set $new_path $captured_path;
more_set_input_headers "Destination: http://$new_path";
}

if (-d $webdav_root/$uri) {
more_set_input_headers "Destination: http://$new_path/";
rewrite ^(?<captured_path>.*[^/])$ $captured_path/ break;
}
root $webdav_root;
dav_methods DELETE;
}

location @copy_move_handler {
auth_basic "WebDAV";
auth_basic_user_file /etc/nginx/webdav.htpasswd;
internal;
open_file_cache off;

if ($destination ~ ^https?://(?<captured_path>.*)$) {
set $new_path $captured_path;
more_set_input_headers "Destination: http://$new_path";
}

if (-d $webdav_root/$uri) {
more_set_input_headers "Destination: http://$new_path/";
rewrite ^(?<captured_path>.*[^/])$ $captured_path/ break;
}
root $webdav_root;
dav_methods COPY MOVE;
}

}

重启Nginx后重新映射一下,这次应该没有问题~

结论

结论放前面,这个方法有点麻烦!不建议使用Windows自带的磁盘映射挂载WevDav!

必须要挂载的,不用上面的操作,直接参考网上的建议:使用第三方软件(如 RaiDrive, NetDrive, CarotDAV)!

不过有一点就是比如RaiDrive以前没有广告的,现在开始有广告了。只能自行抉择了。

后话

Windows其实对SMB支持非常好,但是SMB在外网简直无法直视。

WebDav呢?其实也有缺点,经过我的测试,在上传大文件(10G以上),在传输到99%时,会卡顿一会儿,大概率是因为上传文件是分块进行的,所有的块上传结束后再整合在一起。SMB就没有这个现象。

最后说说我个人的建议:

局域网环境: 大文件多的直接选择SMB,小文件多的两者都可;视频观看则两者都可;

外网环境: 文件传输两者都可;视频观看直接选择WebDav。

说人话就是,建议SMB和WebDav同时用。SMB负责局域网的文件传输,WebDav外网使用(因为外网受限于带宽,基本不会有很大的文件传输,小文件也能胜任,观看视频什么的更是它的强项)。

写这篇文章的时候头还是有点晕的,如果哪里有问题,欢迎各位领导指正,还是不行的话参考下面的文章,与自己的配置再核对核对。

文章中的配置我测试了一个早上,各个文件都试过,目前拷贝50G的文件也是没问题的。不排除我写文章的时候稀里糊涂的有遗漏的。

参考资料:

software:nginx:webdav [Net Lab]

docker-nginx-webdav-nononsense/nginx.conf at main · dgraziotin/docker-nginx-webdav-nononsense · GitHub