getConfig('webdav'); if(!$pluginOption || $pluginOption['mountWebdav'] == '0'){$config = array();} $this->config = $config; $this->dav = new webdavClient($config);// host/user/password/basePath $this->ioFileOutServer = $config['ioFileOutServer'] != '0'; // 下载是否中转 $this->ioUploadServer = $config['ioUploadServer'] != '0'; // 上传是否中转 $this->davServerKod = false; $this->uploadChunkSize = 1024*1024*5; // patch分片上传时; 分片大小; if(!is_string($config['dav'])){return;} $davSupport = $config['dav'] ? $config['dav']:''; $davSupport = explode(',',$davSupport); foreach($davSupport as $key => $type){$davSupport[$key] = trim($type);} if(in_array('extended-kodbox',$davSupport)){$this->davServerKod = true;} } public function mkdir($dir,$repeat=REPEAT_SKIP){ $parent = $dir;$add = array();// 循环创建文件夹; while($parent && $parent != '/' && !$this->exist($parent)){ $name = get_path_this($parent); $parent = get_path_father($parent); // 避免 'test/'类型路径parent为test的情况; $add[] = array($parent,$name); if(count(explode('/',trim($parent,'/'))) == 1){break;} } $add = array_reverse($add); for($i=0; $i < count($add); $i++) { $path = rtrim($add[$i][0],'/').'/'.$add[$i][1]; if(!$this->dav->mkdir($path)){break;} } if(count($add) == 0 || $i == count($add) ){ return $this->getPathOuter($dir); } return false; } private function _copyMove($action,$from,$to,$repeat=REPEAT_REPLACE,$newName=''){ if(!$this->exist($from)) return false; $this->mkdir($this->pathFather($to)); if(!$newName){ $newName = get_path_this($from); $newName = $this->fileNameAuto($to,$newName,$repeat,false); } $destPath = rtrim($to,'/').'/'.$newName; if($action == 'copy'){$result = $this->dav->copy($from,$destPath);} if($action == 'move'){$result = $this->dav->move($from,$destPath);} return $this->exist($destPath) ? $this->getPathOuter($destPath):false; } public function moveSameAllow(){} public function move($from,$to,$repeat=REPEAT_REPLACE) { return $this->_copyMove('move',$from,$to,$repeat); } public function copy($from,$to,$repeat=REPEAT_REPLACE) { return $this->_copyMove('copy',$from,$to,$repeat); } public function copyFile($from,$to){return $this->copy($from,$to);} public function moveFile($from,$to){return $this->move($from,$to);} public function remove($path,$toRecycle=true){return $this->dav->delete($path);} public function rename($from,$newName){ $to = get_path_father($from); return $this->_copyMove('move',$from,$to,REPEAT_SKIP,$newName); } public function has($path,$count=false,$isFolder=false){ $info = $this->listPath($path); if(!$info) return false; $children = array( 'hasFolder' => count($info['folderList']), 'hasFile' => count($info['folderList']), ); if($count){return $children;} return $isFolder ? $children['hasFolder'] : $children['hasFile']; } public function listAll($path){ // 优先处理按kod进行尝试请求; $data = $this->dav->propfind($path,'0','X-DAV-ACTION: kodListAll'); $current = $this->_pathInfoParse(_get($data,'data.response',false)); if($current && isset($current['listAllChildren'])){ foreach ($current['listAllChildren'] as &$item){ $arr = explode('/',trim($item['path'],'/')); $pathShow = implode('/',array_slice($arr,1)).($item['folder'] ? '/':''); $item["path"] = rtrim($current['path'],'/').'/'.$pathShow; // 都使用包含上层的方式,兼容1.45前版本; unset($item["filePath"]); };unset($item); return $current['listAllChildren']; } $result = array(); $this->listAllMake($path,$result); return $result; } public function canRead($path) {return $this->exist($path);} public function canWrite($path) {return $this->exist($path);} public function getContent($file){return $this->fileSubstr($file, 0, -1);} public function fileSubstr($file, $start, $length){ $start = $start ? $start : 0; $end = $start + $length - 1; $range = $length > 0 ? 'bytes='.$start.'-'.$end: ''; $tempFile = $this->tempFile(); $result = $this->dav->get($file,$tempFile,$range); $content = $result ? file_get_contents($tempFile):false; $this->tempFileRemve($tempFile); return $content; } public function mkfile($path,$content='',$repeat = REPEAT_RENAME){ $tempFile = $this->tempFile('',$content); $result = $this->upload($path,$tempFile,false,$repeat); $this->tempFileRemve($tempFile); // io 添加时检测; 根目录新建空文件则放过 if(trim($path,'/') == 'index.html' && !$content){return $this->getPathOuter('/index.html');} return $result; } public function setContent($file, $content = ''){ return $this->mkfile($file,$content,REPEAT_REPLACE) ? true : false; } public function upload($destPath,$localPath,$moveFile=false,$repeat=REPEAT_REPLACE){ if($this->davServerKod && filesize($localPath) >= $this->uploadChunkSize ){ return $this->uploadChunk($destPath,$localPath,$moveFile,$repeat); } $savePath = $this->pathFather($destPath);$this->mkdir($savePath); $saveName = $this->fileNameAuto($savePath,$this->pathThis($destPath),$repeat,false); $destPath = rtrim($savePath,'/').'/'.$saveName; $result = $this->dav->put($destPath,$localPath); // write_log([$destPath,$localPath,$result],'dav'); return $result['status'] ? $this->getPathOuter($destPath) : false; } // 如若是kod,前端直传; public function uploadLink($destPath,$size=0){ $pose = strpos($this->config['host'],'/index.php/plugin/webdav/'); $server = $pose ? substr($pose,0,$pose+1):''; if($this->ioUploadServer) return; if(!$this->davServerKod || !$server || $size <= 1024*10) return; $in = $GLOBALS['in']; $args = array( 'size' => $size, 'uploadWeb' =>'1', 'fullPath' => _get($in,'fullPath',''), 'checkType' => _get($in,'checkType',''),// 必须是'checkHash', 'checkHashSimple' => _get($in,'checkHashSimple',''), ); if($args['checkType'] != 'checkHash') return; $result = $this->uploadSend($destPath,'',$args); if(!$result['status'] || !is_array($result['data']['info'])){ show_json(IO::getLastError(),false); } $uploadInfo = $result['data']['info']; $uploadInfo['webdavUploadTo'] = $uploadInfo['addUploadParam']; unset($uploadInfo['addUploadParam']); // write_log($uploadInfo,'upload'); return $uploadInfo; } private function uploadChunk($destPath,$localPath,$moveFile=false,$repeat=REPEAT_REPLACE){ $savePath = $this->pathFather($destPath);$this->mkdir($savePath); $saveName = $this->fileNameAuto($savePath,$this->pathThis($destPath),$repeat,false); $destPath = rtrim($savePath,'/').'/'.$saveName; // 秒传检测处理; $checkResult = $this->uploadHashCheck($destPath,$localPath); if($checkResult == false){ $checkResult = $this->uploadWithChunk($destPath,$localPath); } return $checkResult['code'] ? $this->getPathOuter($destPath) : false; } /** * 上传处理 * * 1. 上传请求 simpleHash; [checkType=checkHash, path,name,checkHashSimple] * 2. 如果simpleHash匹配到simpleHash则秒传请求; [checkType=matchMd5, path,name,checkHashMd5] * * 正常分片上传 [path,name,size,chunkSize,chunks,chunk]; * data.info字段是否为非空字符串(目标文件路径),来确认文件秒传是否成功 */ private function uploadHashCheck($destPath,$localPath){ // 检测hashSimple $hashSimple = IO::hashSimple($localPath); $args = array('checkType'=>'checkHash','checkHashSimple'=>$hashSimple); $result = $this->uploadSend($destPath,'',$args); if(!is_array($result['data']) || !array_key_exists('code',$result['data'])) return false; if(!$result['data']['code']) return $result['data'];//结束; $hashInfo = _get($result['data'],'info.checkFileHash'); if(!is_array($hashInfo) || !$hashInfo['hashMd5']) return false; if($hashInfo['uploadChunkSize']){$this->uploadChunkSize = $hashInfo['uploadChunkSize'];} $fileMd5 = IO::hashMd5($localPath); if($hashInfo['hashMd5'] != $fileMd5) return false; // 秒传检测 $args = array('checkType'=>'matchMd5','checkHashMd5'=>$fileMd5,'checkHashSimple'=>$hashSimple); $result = $this->uploadSend($destPath,'',$args); if(!is_array($result['data'])) return array('code'=>false,'data'=>$result['error']); return $result['data']; } private function uploadWithChunk($destPath,$localPath){ $totalSize = @filesize($localPath); $chunkSize = $this->uploadChunkSize; $chunkCount = ceil($totalSize / $chunkSize);$chunkIndex = 0; while($chunkIndex < $chunkCount){ $content = IO::fileSubstr($localPath,$chunkIndex * $chunkSize,$chunkSize); $tempFile = $this->tempFile('',$content); $args = array('size'=>$totalSize,'chunkSize'=>$chunkSize,'chunks'=>$chunkCount,'chunk'=>$chunkIndex); $result = $this->uploadSend($destPath,$tempFile,$args);$chunkIndex++; if(!is_array($result['data'])) return array('code'=>false,'data'=>$result['error']); if(!$result['data']['code']) return $result['data'];//有失败情况,则直接结束; } return $result; } private function uploadSend($destPath,$tempFile,$args){ $this->dav->setHeader('X-DAV-UPLOAD','kodbox'); $this->dav->setHeader('X-DAV-ARGS',base64_encode(json_encode($args))); return $this->dav->put($destPath,$tempFile); } public function download($file, $destFile){ $result = $this->dav->get($file,$destFile); return $result ? $destFile : false; } public function listPath($path,$simple=false){ $data = $this->dav->propfind($path); if(!$data['status'] || !$data['data'] || !$data['data']['response']) return false; $list = $data['data']['response']; $current = isset($list[0]) ? $list[0]:$list; $current = $this->_pathInfoParse($current); $result = array('fileList'=>array(),'folderList'=>array(),'current'=>$current); if(!isset($list[0])) return $result; foreach($list as $index => $val){ if($index == 0 ) continue; $item = $this->_pathInfoParse($val); $type = $item['type'] == 'file' ? 'fileList':'folderList'; $result[$type][] = $item; $key = trim($item['path'],'/'); $this->infoCache[$key] = $item; } // 追加信息处理;extendFileList $extendData = _get($data,'data.extendFileList'); $extendData = $extendData ? $extendData : _get($data,'header.X-extendFileList'); $extendData = $extendData ? $extendData : _get($data,'header.X-EXTENDFILELIST'); if($extendData){ $arr = json_decode(base64_decode($extendData),true); $result = array_merge($result,$arr ? $arr:array()); } if(is_array($result['pageInfo']) && $result['pageInfo']['pageNum'] < 100){ unset($result['pageInfo']); } // pr($data,$result);exit; return $result; } public function fileOut($path, $download = false, $downFilename = false, $etag=''){ if($this->fileOutKod($path)) return; $this->fileOutServer($path, $download, $downFilename, $etag); } public function fileOutServer($path, $download = false, $downFilename = false, $etag=''){ parent::fileOut($path, $download, $downFilename, $etag); } public function fileOutImage($path,$width=250){ if($this->fileOutKod($path)) return; parent::fileOutImage($path,$width); } public function fileOutImageServer($path,$width=250){ parent::fileOutImage($path,$width); } private function fileOutKod($path){ if(!$this->davServerKod) return false; if($this->isFileOutServer()) return false; $data = $this->info($path); if(!$data || !$data['fileOutLink']) return false; $link = $data['fileOutLink']; $param = parse_url_query(this_url()); $disableKey = array('accessToken','path'); foreach($param as $key=>$val){ if(!$val || in_array($key,$disableKey)) continue; $link .= '&'.$key.'='.$val; } $this->fileOutLink($link); } // 缓存处理; public static $infoCache = array(); public static $listCache = array(); private function _pathInfo($path,$cacheInfo=false){ if(!$this->pathCheck($path)) return false; $key = trim($path,'/'); if($cacheInfo){self::$infoCache[$key] = $cacheInfo;return;} if(isset(self::$infoCache[$key])) return self::$infoCache[$key]; $data = $this->listPath($path); $pathInfo = $data ? $data['current']:false; if($pathInfo){self::$infoCache[$key] = $pathInfo;} return $pathInfo; } // 文件属性; name/path/type/size/createTime/modifyTime/ private function _pathInfoParse($item){ if(!$item || !isset($item['href'])){return array();} $path = $this->dav->uriToPath($item['href']); $info = array('name'=>get_path_this($path),'path'=>$path,'type'=>'folder'); $prop = _get($item,'propstat.prop',array()); if(is_array($item['propstat']) && is_array($item['propstat'][0])){ $prop = $item['propstat'][0]['prop']; } if(isset($prop['getlastmodified'])){ $info['modifyTime'] = strtotime($prop['getlastmodified']); } if(isset($prop['creationdate'])){ $info['createTime'] = strtotime($prop['creationdate']); } if(isset($prop['getcontentlength'])){ $info['size'] = $prop['getcontentlength']; } $info['type'] = $prop['resourcetype'] == '' ? 'file':'folder'; $info['name'] = $info['name'] ? $info['name']:'/'; $info['path'] = $this->getPathOuter($info['path']); $mimeType = $prop['getcontenttype']; if($mimeType){$info['type'] = ($mimeType == 'httpd/unix-directory') ? 'folder':'file';} $info['_infoSimple'] = true; if($info['type'] == 'file'){ $info['ext'] = get_path_ext($info['name']); } if(isset($prop['extendFileInfo'])){ $arr = json_decode(base64_decode($prop['extendFileInfo']),true); $info = array_merge($info,$arr ? $arr:array()); } return $info; } public function fileInfo($path,$simple=false,$fileInfo = array()) { return $this->_pathInfo($path); } public function folderInfo($path,$simple=false,$itemInfo=array()) { return $this->_pathInfo($path); } public function size($path){ $info = $this->_pathInfo($path); return $info ? $info['size'] : 0; } public function info($path){ return $this->_pathInfo($path); } public function infoWithChildren($path){ $this->dav->setHeader('X-DAV-ACTION','infoChildren'); $info = $this->_pathInfo($path); if($this->davServerKod) return $info; if(is_array($info) && is_array($info['children'])) return $info; return parent::infoWithChildren($path); } public function exist($path){ $info = $this->_pathInfo($path); return $info ? true : false; } public function isFile($path){ $info = $this->_pathInfo($path); return ($info && $info['type'] == 'file') ? true : false; } public function isFolder($path){ $info = $this->_pathInfo($path); return ($info && $info['type'] == 'folder') ? true : false; } private function pathCheck($path){ $PATH_LENGTH_MAX = 4096;//路径最长限制; return strlen($path) >= $PATH_LENGTH_MAX ? false:true; } }