文件上传类
<?php /** * 文件上传 * * 前台使用 * <link rel='stylesheet' type='text/css' href='/Public/static/kcs/kcs.css'> * <script src='/Public/static/kcs/kcs.src.js'></script> * <script src='/Public/static/kcs/kcs.js'></script> * <script> KCS.uploader(el,{ '120', height:'120', url:'{:U('upload')}'}); </script> */ class Upload { const CHUNK_UPLOADED = 1; const FILE_UPLOADED = 2; // 配置项 : 允许上传的文件类型,不区分大小写 protected $allowedType = array( 'jpg', 'jpeg', 'png', 'bmp', 'gif', 'txt', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'zip', 'rar', 'mp3', 'wav', 'csv' ); // 配置项 : 临时文件过期时间, 单位秒(s) protected $tempFileExpire = 3600; // 配置项 : 文件上传完毕后移动到此目录, 默认不移动 protected $uploadDir = null; // 配置项 : 文件自动保存目录的 web 路径, 未配置时使用 /+$uploadDir 作为默认值 protected $uploadPath = null; // 配置项 : 文件上传临时目录, 必填 protected $tempDir = null; // 配置项 : 文件临时目录的 web 路径, 未配置时使用 /+$tempDir 作为默认值 protected $tempPath = null; // 配置项 : 上传成功回调 protected $success = null; // 配置项 : 上传错误回调 protected $error = null; // 当前上传文件的[临时]文件名 protected $filename = null; // 当前上传文件的[原始]文件名 protected $filenameOri = null; // 当前上传文件的[存储]文件名 protected $filenameSave = null; // 当前文件类型 protected $fileType = null; // 错误通知类型 protected static $notifyInfo = array( 1000 => '无法获取文件名', 1001 => '文件名不合法', 1002 => '创建临时文件失败', 1003 => '文件上传错误', 1004 => '文件未上传', 1005 => '无法读取上传临时文件', 1006 => '无法读取输入流', 1007 => '无法移动文件到上传目录', 1008 => '无法移动文件到临时目录', 1009 => '创建上传目录失败', 1010 => '未设置临时目录', 1011 => '创建临时目录失败', 1012 => '文件类型错误' ); protected $application; protected $webPath; static $instace; public static function getInstance () { return self::$instace ? : self::$instace = new self(); } /** * 文件上传处理流程 */ public function handle ($config = array(), $return = true) { try { $this->_loadConfig($config); // 获取文件信息 $this->_loadFileinfo(); // 文件保存路径 $filePath = $this->tempDir . $this->filename . '.part'; // 清理过期文件 // $this->_cleanUp(); // 分段上传 : 当前分段序号 $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0; // 分段上传 : 分段总数 $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0; // 打开或创建临时文件 $out = fopen("{$filePath}", $chunks ? "ab" : "wb") or $this->_notify(1002); // 从上传文件或输入中读取数据 if (! empty($_FILES) && isset($_FILES['kcs_uploader_file'])) { $_FILES["kcs_uploader_file"]["error"] and $this->_notify(1003, $_FILES["file"]["error"]); is_uploaded_file($_FILES["kcs_uploader_file"]["tmp_name"]) or $this->_notify(1004); $in = fopen($_FILES["kcs_uploader_file"]["tmp_name"], "rb") or $this->_notify(1005); } else { $in = fopen("php://input", "rb") or $this->_notify(1006); } // 追加到临时文件 while ($buff = fread($in, 4096)) { fwrite($out, $buff); } fclose($out); fclose($in); // 分段上传 if ($chunks && $chunk < $chunks - 1) { return $this->_notify(self::CHUNK_UPLOADED); } // 上传完毕 if (! $chunks || $chunk == $chunks - 1) { // 重命名上传文件 $newFilename = (is_null($this->filenameSave) ? uniqid(rand(100, 999)) : $this->filenameSave) . '.' . $this->fileType; // 上传图片临时路径 $newFile = $this->tempDir . $newFilename; // 上传图片的 web 路径 $webPath = $this->tempPath . $newFilename; // 重命名临时文件 copy($filePath, $newFile) or $this->_notify(1008, $filePath . '=>' . $newFile); unlink($filePath); // web 路径修正 $webPath = str_replace('\', '/', $webPath); // 发送成功通知 $response = array( 'url' => $webPath, 'file' => $newFile, 'title' => $this->filenameOri ); $this->_notify(self::FILE_UPLOADED, $response); } } catch (Exception $e) { $this->_notify(- 1, $e->getMessage()); } } /** * 移动上传文件 */ public function move ($webPath, $config = array()) { $this->_loadConfig($config); $path = $this->tempDir ? : $this->uploadDir; $oldFile = $path . substr($webPath, strlen($path) - 1); $newFile = $this->uploadDir . basename($oldFile); if (! file_exists($oldFile)) { throw new Exception("文件不存在 : $oldFile"); } // 未配置临时目录时, 不需要移动上传文件 $status = $oldFile == $newFile; $status or $status = (copy($oldFile, $newFile) and unlink($oldFile)); if (! $status) { throw new Exception("移动文件失败 : $oldFile -> $newFile"); } $fileInfo = array( 'path' => $newFile, 'url' => $this->uploadPath . basename($newFile), 'md5' => md5_file($newFile), 'sha1' => sha1_file($newFile) ); return $fileInfo; } /** * 加载配置项, 并校验 */ protected function _loadConfig ($config) { is_array($config) or $config = array(); foreach ($config as $_key => $_value) { if (property_exists($this, $_key)) { $this->$_key = $_value; } } // 检查临时目录 ($this->tempDir = $this->tempDir ? : $this->uploadDir) or $this->_notify(1010); if (! file_exists($this->tempDir)) { mkdir($this->tempDir, 0777, true) or $this->_notify(1011, $this->$this->tempDir); } $this->tempDir = rtrim($this->tempDir, '/') . '/'; // 修正临时目录 web 路径 $this->tempPath or $this->tempPath = '/' . trim($this->tempDir, './'); $this->tempPath = rtrim($this->tempPath, '/') . '/'; // 检查上传目录 if (! $this->uploadDir) { return; } // 创建上传目录 if (! file_exists($this->uploadDir)) { mkdir($this->uploadDir, 0777, true) or $this->_notify(1009, $this->uploadDir); } $this->uploadDir = rtrim($this->uploadDir, '/') . '/'; // 修正上传目录 web 路径 $this->uploadPath or $this->uploadPath = '/' . trim($this->uploadDir, './'); $this->uploadPath = rtrim($this->uploadPath, '/') . '/'; } /** * 获得上传文件的文件名和类型并校验 */ protected function _loadFileinfo () { if (isset($_REQUEST["name"])) { $filename = $_REQUEST["name"]; } elseif (! empty($_FILES)) { $filename = @$_FILES["kcs_uploader_file"]["name"]; } else { // 获取文件名失败 $this->_notify(1000); } // 文件名只允许包含 数字,字母,下划线和点号 // preg_match('/^[a-zA-Z0-9_.]+$/', $filename) or $this->_notify(1001, '文件名无效 : ' . $filename); $this->filenameOri = $filename; $filetype = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $this->filename = md5($_REQUEST['kcs_uploader_id'] . $filename) . '.' . $filetype; // 文件类型检测 in_array($filetype, $this->allowedType) or $this->_notify(1012, '文件类型无效 : ' . $filetype); $this->fileType = $filetype; } /** * 清除过期的临时文件, 删除的临时文件写入清理日志 */ protected function _cleanUp ($targetDir) { while (($file = readdir($this->tempDir)) !== false) { $tmpfilePath = $this->tempDir . $file; // Remove temp file if it is older than the max age and is not the current file if (filemtime($tmpfilePath) < time() - $this->tempFileExpire) { $filesize = number_format(filesize($tmpfilePath) / (1024 * 1024), 2) . 'KB'; @$status = unlink($tmpfilePath); self::_log("清理过期临时文件 [" . ($status ? '1' : '0') . "] : $file (" . $filesize . ")"); } } closedir($dir); } /** * 发送通知 */ protected function _notify ($status = '', $info = null) { $json = array( 'jsonrpc' => '2.0', 'id' => 'id' ); // 分段上传完毕 if ($status == self::CHUNK_UPLOADED) { exit(json_encode($json)); } // 文件上传完毕 if ($status == self::FILE_UPLOADED) { $json['result'] = $info; if (is_callable($this->success)) { return call_user_func($this->success, $json); } exit(json_encode($json)); } // 返回错误信息 $msg = self::$notifyInfo[$status] ? : $info; $json['error'] = array( 'code' => $status, 'message' => $msg ); self::_log($msg . ' | ' . $info); if (is_callable($this->error)) { return call_user_func($this->error, $msg); } exit(json_encode($json)); } protected function _log ($msg) { function_exists('_log') && _log($msg); } }
调用文件上传
1 <?php 2 $operate=$_REQUEST["operate"]; 3 $operate=empty($operate)?"upload":$operate; 4 5 switch ($operate){ 6 case 'upload'; 7 file_upload(); 8 break; 9 case 'download'; 10 file_download(); 11 break; 12 case 'del'; 13 file_del(); 14 break; 15 } 16 17 function file_download(){ 18 $file_code=intval($_REQUEST["file_code"]); 19 $sql="select FILE_PATH,FILE_NAME from app_file where DOWN_CODE='{$file_code}' and STATUS='1'"; 20 $file_info=N_query($sql); 21 if(!empty($file_info)){ 22 $file_info=$file_info[1]; 23 //以只读和二进制模式打开文件 24 $file = fopen ( $file_info["FILE_PATH"], "rb" ); 25 26 //告诉浏览器这是一个文件流格式的文件 27 Header ( "Content-type: application/octet-stream" ); 28 //请求范围的度量单位 29 Header ( "Accept-Ranges: bytes" ); 30 //Content-Length是指定包含于请求或响应中数据的字节长度 31 Header ( "Accept-Length: " . filesize ( $file_info["FILE_PATH"] ) ); 32 //用来告诉浏览器,文件是可以当做附件被下载,下载后的文件名称为$file_name该变量的值。 33 Header ( "Content-Disposition: attachment; filename=" . $file_info["FILE_NAME"] ); 34 35 //读取文件内容并直接输出到浏览器 36 echo fread ( $file, filesize ( $file_info["FILE_PATH"] ) ); 37 fclose ( $file ); 38 } 39 } 40 41 function file_del(){ 42 $file_code=intval($_REQUEST["file_code"]); 43 $sql="select FILE_PATH,FILE_NAME from app_file where DOWN_CODE='{$file_code}' and STATUS='1'"; 44 $file_info=N_query($sql); 45 if(!empty($file_info)){ 46 $file_info=$file_info[1]; 47 $current_time=time(); 48 $sql="update app_file set STATUS='0',DEL_TIME={$current_time} where DOWN_CODE='{$file_code}' and STATUS='1'"; 49 N_query($sql); 50 @unlink( $file_info["FILE_PATH"]); 51 } 52 $return_result["status"]=1; 53 $return_result["msg"]="删除成功"; 54 echo json_encode($return_result,256); 55 exit; 56 57 } 58 59 function file_upload(){ 60 $type_arr=["projectLending"]; 61 $type=trim($_REQUEST["type"]); 62 63 $type=empty($type)?"projectLending":$type; 64 if(!in_array($type,$type_arr)){ 65 throw new Exception("参数错误"); 66 } 67 68 if($type=="projectLending"){ 69 //项目放款流程 70 uploadProjectLending(); 71 } 72 } 73 74 function uploadProjectLending(){ 75 $config = array( 76 // 文件上传目录, 默认不移动 77 'uploadDir' => './uploads/projectLending/' . date('Y/m/d'), 78 'success' => 'uploadProjectLendingSuccessReturn', 79 'error' => 'uploadProjectLendingErrorReturn', 80 //"allowedType"=>['xls','xlsx','csv'], 81 ); 82 NUpload::getInstance()->handle($config); 83 } 84 function uploadProjectLendingErrorReturn($result){ 85 $return_result["status"]=1; 86 $return_result["msg"]=$result; 87 echo json_encode($return_result,256); 88 exit; 89 } 90 function uploadProjectLendingSuccessReturn($result){ 91 $now_time=time(); 92 $info = $result['result']; 93 $USR_UID=$_SESSION['USER_LOGGED']; 94 $APP_UID=trim($_REQUEST["app_uid"]); 95 $TAS_UID=trim($_REQUEST["tas_uid"]); 96 $UPLOAD_TYPE='projectLending'; 97 $FILE_NAME=$info['title']; 98 $FILE_PATH=$info['file']; 99 $FILE_SIZE=$info["file_size"]; 100 $FILE_EXT=$info["file_ext"]; 101 $ISIMAGEL=0; 102 $STATUS=1; 103 $UPLOAD_TIME=$now_time; 104 $DEL_TIME=0; 105 $DOWN_CODE=md5($USR_UID."|".$now_time); 106 107 $sql="insert into app_file (`USR_UID`,`APP_UID`,`TAS_UID`,`UPLOAD_TYPE`,`FILE_NAME`,`FILE_PATH`,`FILE_SIZE`,`FILE_EXT`,`ISIMAGEL`,`STATUS`,`UPLOAD_TIME`,`DEL_TIME`,`DOWN_CODE`) VALUES ( 108 '{$USR_UID}','{$APP_UID}','{$TAS_UID}','{$UPLOAD_TYPE}','{$FILE_NAME}','{$FILE_PATH}','{$FILE_SIZE}','{$FILE_EXT}','{$ISIMAGEL}','{$STATUS}','{$UPLOAD_TIME}','{$DEL_TIME}','{$DOWN_CODE}')"; 109 N_query($sql); 110 111 112 $return_result["status"]=0; 113 $return_result["msg"]="上传成功"; 114 $return_result["url"]=$info['url']; 115 $return_result["file_ext"]=$info['file_ext']; 116 $return_result["file_size"]=$info['file_size']; 117 $return_result["title"]=$info['title']; 118 $return_result["file_id"]=$DOWN_CODE; 119 $return_result["time"]=date("Y-m-d H:i:s",$now_time); 120 $return_result["download_url"]="/sysworkflow/zh/neoclassic/ncfpms_ajax/uploadFile?operate=download&file_code=".$DOWN_CODE; 121 $return_result["del_url"]="/sysworkflow/zh/neoclassic/ncfpms_ajax/uploadFile?operate=del&file_code=".$DOWN_CODE; 122 echo json_encode($return_result); 123 exit; 124 }