• 文件上传


    文件上传类

    <?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 }
  • 相关阅读:
    敏捷21天打卡-AARRR模型
    敏捷21天打卡-精益产品开发最佳实践 之 “AB测试"
    21天敏捷打卡-MVP
    敏捷21天打卡-精益产品开发最佳实践 之 “电梯演讲"
    敏捷21天打卡-精益画布
    敏捷21天打卡--精益产品开发
    21天敏捷打卡--敏捷方法实现
    敏捷21天打卡-在敏捷环境中交付
    敏捷21天打卡-创建敏捷环境
    敏捷21天打卡-生命周期
  • 原文地址:https://www.cnblogs.com/saonian/p/10194260.html
Copyright © 2020-2023  润新知