开启辅助访问     
收藏本站

站内搜索

搜索

Minecraft(我的世界)苦力怕论坛

[其他] php实现webdav服务器

 发表于 2025-11-27 12:19:52|显示全部楼层|阅读模式 IP:广西
自己手头上有些虚拟主机,想挂载到openlist用,很不巧有几台机的ftp是连不上的,所以想到了webdav,于是让ai帮忙搓了个php版的webdav服务器,测试openlist可正常挂载并管理文件
不建议上传过大的文件,否则可能导致姬子爆内存

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

  1. <?php
  2. // 可启用错误报告以便调试
  3. error_reporting(E_ALL);
  4. ini_set('display_errors', 1);

  5. // 身份验证配置
  6. $AUTH_CONFIG = [
  7.     'enabled' => true,
  8.     'realm' => 'WebDAV Server',
  9.     'users' => [
  10.         'admin' => password_hash('admin123', PASSWORD_DEFAULT),
  11.     ]
  12. ];

  13. // 身份验证函数
  14. function authenticate() {
  15.     global $AUTH_CONFIG;
  16.     if (!$AUTH_CONFIG['enabled']) {
  17.         return true;
  18.     }
  19.      
  20.     if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
  21.         header('WWW-Authenticate: Basic realm="' . $AUTH_CONFIG['realm'] . '"');
  22.         header('Content-Type: text/plain; charset=utf-8');
  23.         http_response_code(401);
  24.         echo 'Authentication required';
  25.         exit;
  26.     }
  27.      
  28.     $username = $_SERVER['PHP_AUTH_USER'];
  29.     $password = $_SERVER['PHP_AUTH_PW'];
  30.      
  31.     if (!isset($AUTH_CONFIG['users'][$username]) ||
  32.         !password_verify($password, $AUTH_CONFIG['users'][$username])) {
  33.         header('WWW-Authenticate: Basic realm="' . $AUTH_CONFIG['realm'] . '"');
  34.         http_response_code(401);
  35.         echo 'Authentication failed';
  36.         exit;
  37.     }
  38.      
  39.     return true;
  40. }

  41. // HTTP 状态码函数
  42. function http_code($num) {
  43.     $codes = [
  44.         100 => "HTTP/1.1 100 Continue",
  45.         101 => "HTTP/1.1 101 Switching Protocols",
  46.         200 => "HTTP/1.1 200 OK",
  47.         201 => "HTTP/1.1 201 Created",
  48.         202 => "HTTP/1.1 202 Accepted",
  49.         203 => "HTTP/1.1 203 Non-Authoritative Information",
  50.         204 => "HTTP/1.1 204 No Content",
  51.         205 => "HTTP/1.1 205 Reset Content",
  52.         206 => "HTTP/1.1 206 Partial Content",
  53.         207 => "HTTP/1.1 207 Multi-Status",
  54.         300 => "HTTP/1.1 300 Multiple Choices",
  55.         301 => "HTTP/1.1 301 Moved Permanently",
  56.         302 => "HTTP/1.1 302 Found",
  57.         303 => "HTTP/1.1 303 See Other",
  58.         304 => "HTTP/1.1 304 Not Modified",
  59.         305 => "HTTP/1.1 305 Use Proxy",
  60.         307 => "HTTP/1.1 307 Temporary Redirect",
  61.         400 => "HTTP/1.1 400 Bad Request",
  62.         401 => "HTTP/1.1 401 Unauthorized",
  63.         402 => "HTTP/1.1 402 Payment Required",
  64.         403 => "HTTP/1.1 403 Forbidden",
  65.         404 => "HTTP/1.1 404 Not Found",
  66.         405 => "HTTP/1.1 405 Method Not Allowed",
  67.         406 => "HTTP/1.1 406 Not Acceptable",
  68.         407 => "HTTP/1.1 407 Proxy Authentication Required",
  69.         408 => "HTTP/1.1 408 Request Time-out",
  70.         409 => "HTTP/1.1 409 Conflict",
  71.         410 => "HTTP/1.1 410 Gone",
  72.         411 => "HTTP/1.1 411 Length Required",
  73.         412 => "HTTP/1.1 412 Precondition Failed",
  74.         413 => "HTTP/1.1 413 Request Entity Too Large",
  75.         414 => "HTTP/1.1 414 Request-URI Too Large",
  76.         415 => "HTTP/1.1 415 Unsupported Media Type",
  77.         416 => "HTTP/1.1 416 Requested range not satisfiable",
  78.         417 => "HTTP/1.1 417 Expectation Failed",
  79.         500 => "HTTP/1.1 500 Internal Server Error",
  80.         501 => "HTTP/1.1 501 Not Implemented",
  81.         502 => "HTTP/1.1 502 Bad Gateway",
  82.         503 => "HTTP/1.1 503 Service Unavailable",
  83.         504 => "HTTP/1.1 504 Gateway Time-out"
  84.     ];
  85.     return isset($codes[$num]) ? $codes[$num] : "HTTP/1.1 500 Internal Server Error";
  86. }

  87. function response_http_code($num) {
  88.     header(http_code($num));
  89. }

  90. // XML 响应生成函数
  91. function response_basedir($dir, $lastmod, $status) {
  92.     $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
  93.     return <<<EOF
  94. <d:response>
  95.     <d:href>{$dir}</d:href>
  96.     <d:propstat>
  97.         <d:prop>
  98.             <d:getlastmodified>{$lastmod}</d:getlastmodified>
  99.             <d:resourcetype>
  100.                 <d:collection/>
  101.             </d:resourcetype>
  102.         </d:prop>
  103.         <d:status>{$status}</d:status>
  104.     </d:propstat>
  105. </d:response>
  106. EOF;
  107. }

  108. function response_dir($dir, $lastmod, $status) {
  109.     $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
  110.     return <<<EOF
  111. <d:response>
  112.     <d:href>{$dir}</d:href>
  113.     <d:propstat>
  114.         <d:prop>
  115.             <d:resourcetype>
  116.                 <d:collection/>
  117.             </d:resourcetype>
  118.             <d:getlastmodified>{$lastmod}</d:getlastmodified>
  119.             <d:displayname/>
  120.         </d:prop>
  121.         <d:status>{$status}</d:status>
  122.     </d:propstat>
  123. </d:response>
  124. EOF;
  125. }

  126. function response_file($file_path, $lastmod, $file_length, $status) {
  127.     $lastmod = gmdate("D, d M Y H:i:s", $lastmod)." GMT";
  128.     $tag = md5($lastmod.$file_path);
  129.     return <<<EOF
  130. <d:response>
  131.     <d:href>{$file_path}</d:href>
  132.     <d:propstat>
  133.         <d:prop>
  134.             <d:resourcetype/>
  135.             <d:getcontentlength>{$file_length}</d:getcontentlength>
  136.             <d:getetag>"{$tag}"</d:getetag>
  137.             <d:getcontenttype>application/octet-stream</d:getcontenttype>
  138.             <d:displayname/>
  139.             <d:getlastmodified>{$lastmod}</d:getlastmodified>
  140.         </d:prop>
  141.         <d:status>{$status}</d:status>
  142.     </d:propstat>
  143. </d:response>
  144. EOF;
  145. }

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

  152. class dav {
  153.     protected $public;
  154.     protected $current_user;

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

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

  171.     public function head() {
  172.         if (!authenticate()) return;
  173.          
  174.         $path = $this->getRequestPath();
  175.         if (is_file($path)) {
  176.             header('Content-Type: application/octet-stream');
  177.             header('Content-Length: ' . filesize($path));
  178.             $lastmod = filemtime($path);
  179.             header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmod) . " GMT");
  180.         } else {
  181.             response_http_code(404);
  182.         }
  183.     }

  184.     public function get() {
  185.         if (!authenticate()) return;
  186.          
  187.         $path = $this->getRequestPath();
  188.         if (is_file($path)) {
  189.             header('Content-Type: application/octet-stream');
  190.             readfile($path);
  191.         } else {
  192.             response_http_code(404);
  193.         }
  194.     }

  195.     public function put() {
  196.         if (!authenticate()) return;
  197.          
  198.         $path = $this->getRequestPath();
  199.         $dir = dirname($path);
  200.          
  201.         if (!is_dir($dir)) {
  202.             mkdir($dir, 0755, true);
  203.         }
  204.          
  205.         $input = fopen("php://input", 'r');
  206.         $output = fopen($path, 'w');
  207.          
  208.         if ($input && $output) {
  209.             stream_copy_to_stream($input, $output);
  210.             fclose($input);
  211.             fclose($output);
  212.             response_http_code(201);
  213.         } else {
  214.             response_http_code(500);
  215.         }
  216.     }

  217.     public function propfind() {
  218.         if (!authenticate()) return;
  219.          
  220.         try {
  221.             $path = $this->getRequestPath();
  222.             
  223.             if (!file_exists($path)) {
  224.                 response_http_code(404);
  225.                 return;
  226.             }
  227.             
  228.             $depth = isset($_SERVER['HTTP_DEPTH']) ? (int)$_SERVER['HTTP_DEPTH'] : 1;
  229.             $dav_base_dir = $this->getDavBasePath();
  230.             
  231.             $response_text = '';
  232.             
  233.             if ($depth === 0) {
  234.                 // 只返回请求的资源本身
  235.                 if (is_file($path)) {
  236.                     $response_text = response_file(
  237.                         $dav_base_dir,
  238.                         filemtime($path),
  239.                         filesize($path),
  240.                         http_code(200)
  241.                     );
  242.                 } else {
  243.                     $response_text = response_basedir(
  244.                         $dav_base_dir,
  245.                         filemtime($path),
  246.                         http_code(200)
  247.                     );
  248.                 }
  249.             } else {
  250.                 // Depth 1 或更高 - 返回资源及其直接子项
  251.                 $response_text = response_basedir(
  252.                     $dav_base_dir,
  253.                     filemtime($path),
  254.                     http_code(200)
  255.                 );
  256.                  
  257.                 if (is_dir($path)) {
  258.                     $files = scandir($path);
  259.                      
  260.                     foreach ($files as $file) {
  261.                         if ($file === '.' || $file === '..') {
  262.                             continue;
  263.                         }
  264.                         
  265.                         $file_path = $path . '/' . $file;
  266.                         $file_dav_path = $dav_base_dir . '/' . rawurlencode($file);
  267.                         
  268.                         if (is_dir($file_path)) {
  269.                             $response_text .= response_dir(
  270.                                 $file_dav_path,
  271.                                 filemtime($file_path),
  272.                                 http_code(200)
  273.                             );
  274.                         } else {
  275.                             $response_text .= response_file(
  276.                                 $file_dav_path,
  277.                                 filemtime($file_path),
  278.                                 filesize($file_path),
  279.                                 http_code(200)
  280.                             );
  281.                         }
  282.                     }
  283.                 }
  284.             }
  285.             
  286.             response_http_code(207);
  287.             header('Content-Type: text/xml; charset="utf-8"');
  288.             echo response($response_text);
  289.             
  290.         } catch (Exception $e) {
  291.             response_http_code(500);
  292.         }
  293.     }

  294.     public function delete() {
  295.         if (!authenticate()) return;
  296.          
  297.         $path = $this->getRequestPath();
  298.         if (file_exists($path)) {
  299.             if (is_dir($path)) {
  300.                 $this->deleteDirectory($path);
  301.             } else {
  302.                 unlink($path);
  303.             }
  304.             response_http_code(200);
  305.         } else {
  306.             response_http_code(404);
  307.         }
  308.     }
  309.      
  310.     private function deleteDirectory($dir) {
  311.         if (!is_dir($dir)) return false;
  312.          
  313.         $files = array_diff(scandir($dir), ['.', '..']);
  314.         foreach ($files as $file) {
  315.             $path = $dir . '/' . $file;
  316.             if (is_dir($path)) {
  317.                 $this->deleteDirectory($path);
  318.             } else {
  319.                 unlink($path);
  320.             }
  321.         }
  322.         return rmdir($dir);
  323.     }

  324.     public function lock() {
  325.         if (!authenticate()) return;
  326.         response_http_code(501);
  327.     }

  328.     public function proppatch() {
  329.         if (!authenticate()) return;
  330.         response_http_code(501);
  331.     }

  332.     public function mkcol() {
  333.         if (!authenticate()) return;
  334.          
  335.         $path = $this->getRequestPath();
  336.         if (!file_exists($path)) {
  337.             if (mkdir($path, 0755, true)) {
  338.                 response_http_code(201);
  339.             } else {
  340.                 response_http_code(500);
  341.             }
  342.         } else {
  343.             response_http_code(405);
  344.         }
  345.     }

  346.     public function move() {
  347.         if (!authenticate()) return;
  348.          
  349.         $source = $this->getRequestPath();
  350.         $destination = isset($_SERVER['HTTP_DESTINATION']) ? $this->parseDestination($_SERVER['HTTP_DESTINATION']) : null;
  351.          
  352.         if ($destination && file_exists($source)) {
  353.             if (rename($source, $destination)) {
  354.                 response_http_code(201);
  355.             } else {
  356.                 response_http_code(500);
  357.             }
  358.         } else {
  359.             response_http_code(400);
  360.         }
  361.     }
  362.      
  363.     // 辅助方法
  364.     private function getRequestPath() {
  365.         $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
  366.         $relative_path = ltrim($path_info, '/');
  367.         return $this->public . '/' . $relative_path;
  368.     }
  369.      
  370.     private function getDavBasePath() {
  371.         $path_info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
  372.         $script_name = $_SERVER['SCRIPT_NAME'];
  373.          
  374.         // 构建完整的 DAV 路径
  375.         $dav_path = $script_name . $path_info;
  376.         if ($dav_path === '') {
  377.             $dav_path = '/';
  378.         }
  379.          
  380.         // 确保路径以 / 结尾对于目录
  381.         if ($dav_path !== '/' && substr($dav_path, -1) !== '/') {
  382.             $dav_path .= '/';
  383.         }
  384.          
  385.         return $dav_path;
  386.     }
  387.      
  388.     private function parseDestination($destination) {
  389.         // 从 Destination 头中提取路径
  390.         $script_name = $_SERVER['SCRIPT_NAME'];
  391.         $pos = strpos($destination, $script_name);
  392.          
  393.         if ($pos !== false) {
  394.             $relative_path = substr($destination, $pos + strlen($script_name));
  395.             return $this->public . '/' . ltrim($relative_path, '/');
  396.         }
  397.          
  398.         return null;
  399.     }
  400. }

  401. // 主执行流程
  402. try {
  403.     $dav = new dav();
  404.     $request_method = strtolower($_SERVER['REQUEST_METHOD']);
  405.      
  406.     if (method_exists($dav, $request_method)) {
  407.         $dav->$request_method();
  408.     } else {
  409.         response_http_code(405);
  410.         header('Allow: OPTIONS, GET, HEAD, PUT, POST, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK');
  411.     }
  412.      
  413. } catch (Exception $e) {
  414.     http_response_code(500);
  415. }
复制代码
苦力怕论坛,感谢有您~

本版积分规则

本站
关于我们
联系我们
坛史纲要
官方
哔哩哔哩
技术博客
下载
网易版
安卓版
JAVA
反馈
意见建议
教程中心
更多
捐助本站
QQ群
QQ群

QQ群

访问手机版

访问手机版

手机版|小黑屋|系统状态|klpbbs.com

| 由 木韩网络 提供支持 | GMT+8, 2026-2-3 07:33

声明:本站与Mojang以及微软公司没有从属关系

Powered by Discuz! X3.4