吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2313|回复: 14
收起左侧

[其他转载] php实现webdav服务器

[复制链接]
xiaoyonggaoya 发表于 2025-11-26 19:59
本帖最后由 xiaoyonggaoya 于 2025-11-28 12:56 编辑

自己手头上有些虚拟主机,想挂载到openlist用,很不巧有几台机的ftp是连不上的,所以想到了webdav,于是让ai帮忙搓了个php版的webdav服务器,测试openlist可正常挂载并管理文件
不建议上传过大的文件,否则可能导致姬子爆内存

默认连接地址是http或https://你的域名/php文件的名字.php
用户名默认是admin,密码默认是admin123,自行修改第11行的内容更改账号密码
将第8行的true修改为false可以关闭验证
默认管理public文件夹下的文件,替换第166行的/public可以更改目录

apache版

[PHP] 纯文本查看 复制代码
<?php
// 可启用错误报告以便调试
error_reporting(E_ALL);
ini_set('display_errors', 1);

// 身份验证配置
$AUTH_CONFIG = [
    'enabled' => true,
    'realm' => 'WebDAV Server',
    'users' => [
        'admin' => password_hash('admin123', PASSWORD_DEFAULT),
    ]
];

// 身份验证函数
function authenticate() {
    global $AUTH_CONFIG;
    if (!$AUTH_CONFIG['enabled']) {
        return true;
    }
    
    if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
        header('WWW-Authenticate: Basic realm="' . $AUTH_CONFIG['realm'] . '"');
        header('Content-Type: text/plain; charset=utf-8');
        http_response_code(401);
        echo 'Authentication required';
        exit;
    }
    
    $username = $_SERVER['PHP_AUTH_USER'];
    $password = $_SERVER['PHP_AUTH_PW'];
    
    if (!isset($AUTH_CONFIG['users'][$username]) || 
        !password_verify($password, $AUTH_CONFIG['users'][$username])) {
        header('WWW-Authenticate: Basic realm="' . $AUTH_CONFIG['realm'] . '"');
        http_response_code(401);
        echo 'Authentication failed';
        exit;
    }
    
    return true;
}

// HTTP 状态码函数
function http_code($num) {
    $codes = [
        100 => "HTTP/1.1 100 Continue",
        101 => "HTTP/1.1 101 Switching Protocols",
        200 => "HTTP/1.1 200 OK",
        201 => "HTTP/1.1 201 Created",
        202 => "HTTP/1.1 202 Accepted",
        203 => "HTTP/1.1 203 Non-Authoritative Information",
        204 => "HTTP/1.1 204 No Content",
        205 => "HTTP/1.1 205 Reset Content",
        206 => "HTTP/1.1 206 Partial Content",
        207 => "HTTP/1.1 207 Multi-Status",
        300 => "HTTP/1.1 300 Multiple Choices",
        301 => "HTTP/1.1 301 Moved Permanently",
        302 => "HTTP/1.1 302 Found",
        303 => "HTTP/1.1 303 See Other",
        304 => "HTTP/1.1 304 Not Modified",
        305 => "HTTP/1.1 305 Use Proxy",
        307 => "HTTP/1.1 307 Temporary Redirect",
        400 => "HTTP/1.1 400 Bad Request",
        401 => "HTTP/1.1 401 Unauthorized",
        402 => "HTTP/1.1 402 Payment Required",
        403 => "HTTP/1.1 403 Forbidden",
        404 => "HTTP/1.1 404 Not Found",
        405 => "HTTP/1.1 405 Method Not Allowed",
        406 => "HTTP/1.1 406 Not Acceptable",
        407 => "HTTP/1.1 407 Proxy Authentication Required",
        408 => "HTTP/1.1 408 Request Time-out",
        409 => "HTTP/1.1 409 Conflict",
        410 => "HTTP/1.1 410 Gone",
        411 => "HTTP/1.1 411 Length Required",
        412 => "HTTP/1.1 412 Precondition Failed",
        413 => "HTTP/1.1 413 Request Entity Too Large",
        414 => "HTTP/1.1 414 Request-URI Too Large",
        415 => "HTTP/1.1 415 Unsupported Media Type",
        416 => "HTTP/1.1 416 Requested range not satisfiable",
        417 => "HTTP/1.1 417 Expectation Failed",
        500 => "HTTP/1.1 500 Internal Server Error",
        501 => "HTTP/1.1 501 Not Implemented",
        502 => "HTTP/1.1 502 Bad Gateway",
        503 => "HTTP/1.1 503 Service Unavailable",
        504 => "HTTP/1.1 504 Gateway Time-out"
    ];
    return isset($codes[$num]) ? $codes[$num] : "HTTP/1.1 500 Internal Server Error";
}

function response_http_code($num) {
    header(http_code($num));
}

// XML 响应生成函数
function response_basedir($dir, $lastmod, $status) {
    $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
    return <<<EOF
<d:response>
    <d:href>{$dir}</d:href>
    <d:propstat>
        <d:prop>
            <d:getlastmodified>{$lastmod}</d:getlastmodified>
            <d:resourcetype>
                <d:collection/>
            </d:resourcetype>
        </d:prop>
        <d:status>{$status}</d:status>
    </d:propstat>
</d:response>
EOF;
}

function response_dir($dir, $lastmod, $status) {
    $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
    return <<<EOF
<d:response>
    <d:href>{$dir}</d:href>
    <d:propstat>
        <d:prop>
            <d:resourcetype>
                <d:collection/>
            </d:resourcetype>
            <d:getlastmodified>{$lastmod}</d:getlastmodified>
            <d:displayname/>
        </d:prop>
        <d:status>{$status}</d:status>
    </d:propstat>
</d:response>
EOF;
}

function response_file($file_path, $lastmod, $file_length, $status) {
    $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
    $tag = md5($lastmod.$file_path);
    return <<<EOF
<d:response>
    <d:href>{$file_path}</d:href>
    <d:propstat>
        <d:prop>
            <d:resourcetype/>
            <d:getcontentlength>{$file_length}</d:getcontentlength>
            <d:getetag>"{$tag}"</d:getetag>
            <d:getcontenttype>application/octet-stream</d:getcontenttype>
            <d:displayname/>
            <d:getlastmodified>{$lastmod}</d:getlastmodified>
        </d:prop>
        <d:status>{$status}</d:status>
    </d:propstat>
</d:response>
EOF;
}

function response($text) {
    return '<?xml version="1.0" encoding="utf-8"?>' . "\n" .
           '<d:multistatus xmlns:d="DAV:">' . "\n" .
           $text . "\n" .
           '</d:multistatus>';
}

class dav {
    protected $public;
    protected $current_user;

    public function __construct() {
        $this->public = __DIR__ . '/public';
        $this->current_user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
        
        // 确保 public 目录存在
        if (!is_dir($this->public)) {
            mkdir($this->public, 0755, true);
        }
    }

    public function options() {
        header('DAV: 1, 2');
        header('MS-Author-Via: DAV');
        header('Allow: OPTIONS, GET, HEAD, PUT, POST, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK');
        header('Content-Length: 0');
        response_http_code(200);
    }

    public function head() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (is_file($path)) {
            header('Content-Type: application/octet-stream');
            header('Content-Length: ' . filesize($path));
            $lastmod = filemtime($path);
            header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmod) . " GMT");
        } else {
            response_http_code(404);
        }
    }

    public function get() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (is_file($path)) {
            header('Content-Type: application/octet-stream');
            readfile($path);
        } else {
            response_http_code(404);
        }
    }

    public function put() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        $dir = dirname($path);
        
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        
        $input = fopen("php://input", 'r');
        $output = fopen($path, 'w');
        
        if ($input && $output) {
            stream_copy_to_stream($input, $output);
            fclose($input);
            fclose($output);
            response_http_code(201);
        } else {
            response_http_code(500);
        }
    }

    public function propfind() {
        if (!authenticate()) return;
        
        try {
            $path = $this->getRequestPath();
            
            if (!file_exists($path)) {
                response_http_code(404);
                return;
            }
            
            $depth = isset($_SERVER['HTTP_DEPTH']) ? (int)$_SERVER['HTTP_DEPTH'] : 1;
            $dav_base_dir = $this->getDavBasePath();
            
            $response_text = '';
            
            if ($depth === 0) {
                // 只返回请求的资源本身
                if (is_file($path)) {
                    $response_text = response_file(
                        $dav_base_dir, 
                        filemtime($path), 
                        filesize($path), 
                        http_code(200)
                    );
                } else {
                    $response_text = response_basedir(
                        $dav_base_dir, 
                        filemtime($path), 
                        http_code(200)
                    );
                }
            } else {
                // Depth 1 或更高 - 返回资源及其直接子项
                $response_text = response_basedir(
                    $dav_base_dir, 
                    filemtime($path), 
                    http_code(200)
                );
                
                if (is_dir($path)) {
                    $files = scandir($path);
                    
                    foreach ($files as $file) {
                        if ($file === '.' || $file === '..') {
                            continue;
                        }
                        
                        $file_path = $path . '/' . $file;
                        $file_dav_path = $dav_base_dir . '/' . rawurlencode($file);
                        
                        if (is_dir($file_path)) {
                            $response_text .= response_dir(
                                $file_dav_path,
                                filemtime($file_path),
                                http_code(200)
                            );
                        } else {
                            $response_text .= response_file(
                                $file_dav_path,
                                filemtime($file_path),
                                filesize($file_path),
                                http_code(200)
                            );
                        }
                    }
                }
            }
            
            response_http_code(207);
            header('Content-Type: text/xml; charset="utf-8"');
            echo response($response_text);
            
        } catch (Exception $e) {
            response_http_code(500);
        }
    }

    public function delete() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (file_exists($path)) {
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
            response_http_code(200);
        } else {
            response_http_code(404);
        }
    }
    
    private function deleteDirectory($dir) {
        if (!is_dir($dir)) return false;
        
        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = $dir . '/' . $file;
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
        }
        return rmdir($dir);
    }

    public function lock() {
        if (!authenticate()) return;
        response_http_code(501);
    }

    public function proppatch() {
        if (!authenticate()) return;
        response_http_code(501);
    }

    public function mkcol() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (!file_exists($path)) {
            if (mkdir($path, 0755, true)) {
                response_http_code(201);
            } else {
                response_http_code(500);
            }
        } else {
            response_http_code(405);
        }
    }

    public function move() {
        if (!authenticate()) return;
        
        $source = $this->getRequestPath();
        $destination = isset($_SERVER['HTTP_DESTINATION']) ? $this->parseDestination($_SERVER['HTTP_DESTINATION']) : null;
        
        if ($destination && file_exists($source)) {
            if (rename($source, $destination)) {
                response_http_code(201);
            } else {
                response_http_code(500);
            }
        } else {
            response_http_code(400);
        }
    }
    
    // 辅助方法
    private function getRequestPath() {
        $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
        $relative_path = ltrim($path_info, '/');
        return $this->public . '/' . $relative_path;
    }
    
    private function getDavBasePath() {
        $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
        $script_name = $_SERVER['SCRIPT_NAME'];
        
        // 构建完整的 DAV 路径
        $dav_path = $script_name . $path_info;
        if ($dav_path === '') {
            $dav_path = '/';
        }
        
        // 确保路径以 / 结尾对于目录
        if ($dav_path !== '/' && substr($dav_path, -1) !== '/') {
            $dav_path .= '/';
        }
        
        return $dav_path;
    }
    
    private function parseDestination($destination) {
        // 从 Destination 头中提取路径
        $script_name = $_SERVER['SCRIPT_NAME'];
        $pos = strpos($destination, $script_name);
        
        if ($pos !== false) {
            $relative_path = substr($destination, $pos + strlen($script_name));
            return $this->public . '/' . ltrim($relative_path, '/');
        }
        
        return null;
    }
}

// 主执行流程
try {
    $dav = new dav();
    $request_method = strtolower($_SERVER['REQUEST_METHOD']);
    
    if (method_exists($dav, $request_method)) {
        $dav->$request_method();
    } else {
        response_http_code(405);
        header('Allow: OPTIONS, GET, HEAD, PUT, POST, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK');
    }
    
} catch (Exception $e) {
    http_response_code(500);
}


nginx版
注:nginx版修改目录是替换138行的/public,其它没区别

[PHP] 纯文本查看 复制代码
<?php
// 可启用错误报告以便调试
error_reporting(E_ALL);
ini_set('display_errors', 1);

// 身份验证配置
$AUTH_CONFIG = [
    'enabled' => true,
    'realm' => 'WebDAV Server',
    'users' => [
        'admin' => password_hash('admin123', PASSWORD_DEFAULT),
    ]
];

// 身份验证函数
function authenticate() {
    global $AUTH_CONFIG;
    if (!$AUTH_CONFIG['enabled']) {
        return true;
    }
    
    if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
        header('WWW-Authenticate: Basic realm="' . $AUTH_CONFIG['realm'] . '"');
        header('Content-Type: text/plain; charset=utf-8');
        http_response_code(401);
        echo 'Authentication required';
        exit;
    }
    
    $username = $_SERVER['PHP_AUTH_USER'];
    $password = $_SERVER['PHP_AUTH_PW'];
    
    if (!isset($AUTH_CONFIG['users'][$username]) || 
        !password_verify($password, $AUTH_CONFIG['users'][$username])) {
        header('WWW-Authenticate: Basic realm="' . $AUTH_CONFIG['realm'] . '"');
        http_response_code(401);
        echo 'Authentication failed';
        exit;
    }
    
    return true;
}

// HTTP 状态码函数
function http_code($num) {
    $codes = [
        200 => "HTTP/1.1 200 OK",
        201 => "HTTP/1.1 201 Created",
        204 => "HTTP/1.1 204 No Content",
        207 => "HTTP/1.1 207 Multi-Status",
        400 => "HTTP/1.1 400 Bad Request",
        401 => "HTTP/1.1 401 Unauthorized",
        403 => "HTTP/1.1 403 Forbidden",
        404 => "HTTP/1.1 404 Not Found",
        405 => "HTTP/1.1 405 Method Not Allowed",
        500 => "HTTP/1.1 500 Internal Server Error",
        501 => "HTTP/1.1 501 Not Implemented",
        503 => "HTTP/1.1 503 Service Unavailable"
    ];
    return isset($codes[$num]) ? $codes[$num] : "HTTP/1.1 500 Internal Server Error";
}

function response_http_code($num) {
    header(http_code($num));
}

// XML 响应生成函数
function response_basedir($dir, $lastmod, $status) {
    $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
    return <<<EOF
<d:response>
    <d:href>{$dir}</d:href>
    <d:propstat>
        <d:prop>
            <d:getlastmodified>{$lastmod}</d:getlastmodified>
            <d:resourcetype>
                <d:collection/>
            </d:resourcetype>
        </d:prop>
        <d:status>{$status}</d:status>
    </d:propstat>
</d:response>
EOF;
}

function response_dir($dir, $lastmod, $status) {
    $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
    return <<<EOF
<d:response>
    <d:href>{$dir}</d:href>
    <d:propstat>
        <d:prop>
            <d:resourcetype>
                <d:collection/>
            </d:resourcetype>
            <d:getlastmodified>{$lastmod}</d:getlastmodified>
            <d:displayname/>
        </d:prop>
        <d:status>{$status}</d:status>
    </d:propstat>
</d:response>
EOF;
}

function response_file($file_path, $lastmod, $file_length, $status) {
    $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
    $tag = md5($lastmod.$file_path);
    return <<<EOF
<d:response>
    <d:href>{$file_path}</d:href>
    <d:propstat>
        <d:prop>
            <d:resourcetype/>
            <d:getcontentlength>{$file_length}</d:getcontentlength>
            <d:getetag>"{$tag}"</d:getetag>
            <d:getcontenttype>application/octet-stream</d:getcontenttype>
            <d:displayname/>
            <d:getlastmodified>{$lastmod}</d:getlastmodified>
        </d:prop>
        <d:status>{$status}</d:status>
    </d:propstat>
</d:response>
EOF;
}

function response($text) {
    return '<?xml version="1.0" encoding="utf-8"?>' . "\n" .
           '<d:multistatus xmlns:d="DAV:">' . "\n" .
           $text . "\n" .
           '</d:multistatus>';
}

class dav {
    protected $public;
    protected $current_user;

    public function __construct() {
        $this->public = __DIR__ . '/public';
        $this->current_user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
        
        // 确保 public 目录存在
        if (!is_dir($this->public)) {
            mkdir($this->public, 0755, true);
        }
    }

    public function options() {
        header('DAV: 1, 2');
        header('MS-Author-Via: DAV');
        header('Allow: OPTIONS, GET, HEAD, PUT, POST, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK');
        header('Content-Length: 0');
        response_http_code(200);
    }

    public function head() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (is_file($path)) {
            header('Content-Type: application/octet-stream');
            header('Content-Length: ' . filesize($path));
            $lastmod = filemtime($path);
            header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmod) . " GMT");
        } else {
            response_http_code(404);
        }
    }

    public function get() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (is_file($path)) {
            header('Content-Type: application/octet-stream');
            header('Content-Length: ' . filesize($path));
            
            // 设置正确的中文文件名下载头
            $filename = basename($path);
            $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
            
            if (preg_match('/MSIE|Trident/i', $user_agent)) {
                // IE 浏览器
                $filename = rawurlencode($filename);
                header('Content-Disposition: attachment; filename="' . $filename . '"');
            } elseif (preg_match('/Firefox/i', $user_agent)) {
                // Firefox 浏览器
                header('Content-Disposition: attachment; filename*="utf-8\'\'' . $filename . '"');
            } else {
                // 其他浏览器(Chrome, Safari, Edge等)
                header('Content-Disposition: attachment; filename="' . $filename . '"');
            }
            
            readfile($path);
        } else {
            response_http_code(404);
        }
    }

    public function put() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        $dir = dirname($path);
        
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        
        $input = fopen("php://input", 'r');
        $output = fopen($path, 'w');
        
        if ($input && $output) {
            stream_copy_to_stream($input, $output);
            fclose($input);
            fclose($output);
            response_http_code(201);
        } else {
            response_http_code(500);
        }
    }

    public function propfind() {
        if (!authenticate()) return;
        
        try {
            $path = $this->getRequestPath();
            
            if (!file_exists($path)) {
                response_http_code(404);
                return;
            }
            
            $depth = isset($_SERVER['HTTP_DEPTH']) ? (int)$_SERVER['HTTP_DEPTH'] : 1;
            $dav_base_dir = $this->getDavBasePath();
            
            $response_text = '';
            
            if ($depth === 0) {
                // 只返回请求的资源本身
                if (is_file($path)) {
                    $response_text = response_file(
                        $dav_base_dir, 
                        filemtime($path), 
                        filesize($path), 
                        http_code(200)
                    );
                } else {
                    $response_text = response_basedir(
                        $dav_base_dir, 
                        filemtime($path), 
                        http_code(200)
                    );
                }
            } else {
                // Depth 1 或更高 - 返回资源及其直接子项
                $response_text = response_basedir(
                    $dav_base_dir, 
                    filemtime($path), 
                    http_code(200)
                );
                
                if (is_dir($path)) {
                    $files = scandir($path);
                    
                    foreach ($files as $file) {
                        if ($file === '.' || $file === '..') {
                            continue;
                        }
                        
                        $file_path = $path . '/' . $file;
                        
                        // 使用原始文件名而不是URL编码的文件名
                        // 但需要对特殊字符进行适当处理
                        $file_dav_path = $dav_base_dir . $this->encodePath($file);
                        
                        if (is_dir($file_path)) {
                            // 确保目录路径以 / 结尾
                            if (substr($file_dav_path, -1) !== '/') {
                                $file_dav_path .= '/';
                            }
                            
                            $response_text .= response_dir(
                                $file_dav_path,
                                filemtime($file_path),
                                http_code(200)
                            );
                        } else {
                            $response_text .= response_file(
                                $file_dav_path,
                                filemtime($file_path),
                                filesize($file_path),
                                http_code(200)
                            );
                        }
                    }
                }
            }
            
            response_http_code(207);
            header('Content-Type: text/xml; charset="utf-8"');
            echo response($response_text);
            
        } catch (Exception $e) {
            response_http_code(500);
        }
    }

    public function delete() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (file_exists($path)) {
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
            response_http_code(200);
        } else {
            response_http_code(404);
        }
    }
    
    private function deleteDirectory($dir) {
        if (!is_dir($dir)) return false;
        
        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = $dir . '/' . $file;
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
        }
        return rmdir($dir);
    }

    public function lock() {
        if (!authenticate()) return;
        response_http_code(501);
    }

    public function proppatch() {
        if (!authenticate()) return;
        response_http_code(501);
    }

    public function mkcol() {
        if (!authenticate()) return;
        
        $path = $this->getRequestPath();
        if (!file_exists($path)) {
            if (mkdir($path, 0755, true)) {
                response_http_code(201);
            } else {
                response_http_code(500);
            }
        } else {
            response_http_code(405);
        }
    }

    public function move() {
        if (!authenticate()) return;
        
        $source = $this->getRequestPath();
        $destination = isset($_SERVER['HTTP_DESTINATION']) ? $this->parseDestination($_SERVER['HTTP_DESTINATION']) : null;
        
        if ($destination && file_exists($source)) {
            // 确保目标目录存在
            $destDir = dirname($destination);
            if (!is_dir($destDir)) {
                mkdir($destDir, 0755, true);
            }
            
            if (rename($source, $destination)) {
                response_http_code(201);
            } else {
                response_http_code(500);
            }
        } else {
            response_http_code(400);
        }
    }
    
    // 辅助方法 - Nginx 兼容
    private function getRequestPath() {
        // Nginx 环境下获取请求路径
        $request_uri = $_SERVER['REQUEST_URI'];
        $script_name = $_SERVER['SCRIPT_NAME'];
        
        // 提取相对于脚本的路径
        if (strpos($request_uri, $script_name) === 0) {
            $relative_path = substr($request_uri, strlen($script_name));
        } else {
            $relative_path = $request_uri;
        }
        
        $relative_path = ltrim($relative_path, '/');
        
        // 解码 URL 编码的路径部分
        $relative_path = $this->decodePath($relative_path);
        
        $full_path = $this->public . '/' . $relative_path;
        
        // 确保路径在 public 目录内
        $real_public = realpath($this->public);
        $real_full = realpath(dirname($full_path));
        
        if ($real_full === false || strpos($real_full, $real_public) !== 0) {
            return $this->public . '/';
        }
        
        return $full_path;
    }
    
    private function getDavBasePath() {
        // Nginx 环境下构建 DAV 基础路径
        $request_uri = $_SERVER['REQUEST_URI'];
        $script_name = $_SERVER['SCRIPT_NAME'];
        
        // 构建完整的 DAV 路径
        if (strpos($request_uri, $script_name) === 0) {
            $dav_path = $script_name . substr($request_uri, strlen($script_name));
        } else {
            $dav_path = $script_name . $request_uri;
        }
        
        // 规范化路径
        $dav_path = rtrim($dav_path, '/') . '/';
        if ($dav_path === '//') {
            $dav_path = '/';
        }
        
        return $dav_path;
    }
    
    private function parseDestination($destination) {
        // 从 Destination 头中提取路径 - Nginx 兼容
        $script_name = $_SERVER['SCRIPT_NAME'];
        $host = $_SERVER['HTTP_HOST'];
        
        // 构建基础 URL
        $base_url = 'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . $host . $script_name;
        
        if (strpos($destination, $base_url) === 0) {
            $relative_path = substr($destination, strlen($base_url));
            // 解码 URL 编码的路径
            $relative_path = $this->decodePath($relative_path);
            return $this->public . '/' . ltrim($relative_path, '/');
        }
        
        return null;
    }
    
    // 新增:路径编码函数(只对必要字符编码)
    private function encodePath($path) {
        // 只对空格和特殊字符进行编码,保持中文原样
        $search = [' ', '"', '<', '>', '#', '?', '{', '}', '|', '\\', '^', '~', '[', ']', '`'];
        $replace = array_map('rawurlencode', $search);
        return str_replace($search, $replace, $path);
    }
    
    // 新增:路径解码函数
    private function decodePath($path) {
        return rawurldecode($path);
    }
}

// 主执行流程
try {
    $dav = new dav();
    $request_method = strtolower($_SERVER['REQUEST_METHOD']);
    
    if (method_exists($dav, $request_method)) {
        $dav->$request_method();
    } else {
        response_http_code(405);
        header('Allow: OPTIONS, GET, HEAD, PUT, POST, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK');
    }
    
} catch (Exception $e) {
    http_response_code(500);
}

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
Dp270330 + 1 + 1 我很赞同!
WYS66DI + 1 + 1 我很赞同!
5omggx + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

80371521 发表于 2025-11-26 21:48
大佬 现在流行用GO实现
如果python能 计算型多线程 那python是最好不过
node 也是单线程  
其实后端怎么实现都可以 如果不考虑多线程性能的话 python 最方便
 楼主| xiaoyonggaoya 发表于 2026-1-6 21:22
nnpig 发表于 2026-1-5 11:14
免费的虚拟主机好像不行,登录校验界面出来了,然后后面就提示 404 了

你是直接拿浏览器请求的吧,没对浏览器直接get请求文件列表的情况做处理,需要使用可以挂载webdav的软件访问(实在没有用windows文件资源管理器也行)
Xbz123 发表于 2025-11-26 20:21
fengyexue 发表于 2025-11-26 20:55
的确    现在PHP少见
小屎球 发表于 2025-11-26 21:01
php能实现这个也是用心了
 楼主| xiaoyonggaoya 发表于 2025-11-26 21:52
本帖最后由 xiaoyonggaoya 于 2025-11-26 21:54 编辑
80371521 发表于 2025-11-26 21:48
大佬 现在流行用GO实现
如果python能 计算型多线程 那python是最好不过
node 也是单线程  

虚拟主机,只支持php,而且没法修改nginx的配置,所以只能用php玩玩
其它的基本上都有人造轮子了
有些倒是还支持asp,这玩意比php还古早,暂时不清楚怎么下手
zhangSen 发表于 2025-11-26 23:00
用java实现怎么样
头像被屏蔽
tf0721 发表于 2025-11-27 10:35
提示: 作者被禁止或删除 内容自动屏蔽
xiaocaoyf 发表于 2025-11-27 11:16
好东西啊,正好有个php服务器,试试,就是这代码看不懂,学习学习
quary888 发表于 2025-11-27 13:07
收藏了
谢谢楼主分享!好人一生平安!!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-5 21:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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