Hi,为确保您的账号安全,请大家修改登陆密码为包含大小写字母、标点、数字的复合密码,不要使用过于简单的登陆密码。
您当前的位置:首页 :: PHP

php一个上传类,从thinkphp中提取的

时间:2016-10-10 17:34:57  来源:  作者:

php一个上传类,从thinkphp中提取的

方便大家使用,我就分享出来了

<?php
/**
 * 上传类
 * @author jorsh 20160331
 **/

class Upload {

    //上传配置文件
    private $config = array(
        'mimes'         =>  array(), //允许上传的文件MiMe类型
        'maxSize'       =>  0, //上传的文件大小限制 (0-不做限制)
        'exts'          =>  array(), //允许上传的文件后缀
        'autoSub'       =>  true, //自动子目录保存文件
        'subName'       =>  array('date', 'Y-m-d'), //子目录创建方式,[0]-函数名,[1]-参数,多个参数使用数组
        'rootPath'      =>  './Uploads/', //保存根路径
        'savePath'      =>  '', //保存路径
        'saveName'      =>  array('uniqid', ''), //上传文件命名规则,[0]-函数名,[1]-参数,多个参数使用数组
        'saveExt'       =>  '', //文件保存后缀,空则使用原后缀
        'replace'       =>  false, //存在同名是否覆盖
        'hash'          =>  true, //是否生成hash编码
        'callback'      =>  false, //检测文件是否存在回调,如果存在返回文件信息数组
    );

    private $error = ''; //上传错误信息



    public function __construct($config = array()){
        //获取配置
        $this->config   =   array_merge($this->config, $config);
        //调整配置,把字符串配置参数转换为数组
        if(!empty($this->config['mimes'])){
            if(is_string($this->mimes)) {
                $this->config['mimes'] = explode(',', $this->mimes);
            }
            $this->config['mimes'] = array_map('strtolower', $this->mimes);
        }
        if(!empty($this->config['exts'])){
            if (is_string($this->exts)){
                $this->config['exts'] = explode(',', $this->exts);
            }
            $this->config['exts'] = array_map('strtolower', $this->exts);
        }
    }


    //获取最后一次上传错误信息
    public function getError(){
        return $this->error;
    }
    /**
     * 上传单个文件
     * @param  array  $file 文件数组
     * @return array        上传成功后的文件信息
     */
    public function uploadOne($file){
        $info = $this->upload(array($file));
        return $info ? $info[0] : $info;
    }

    /**
     * 上传文件
     * @param 文件信息数组 $files ,通常是 $_FILES数组
     */
    public function upload($files='') {
        if('' === $files){
            $files  =   $_FILES;
        }
        if(empty($files)){
            $this->error = '没有上传的文件!';
            return false;
        }
        //检测上传根目录
        if(!$this->checkRootPath($this->rootPath)){
            return false;
        }

        //检查上传目录
        if(!$this->checkSavePath($this->savePath)){
            return false;
        }

        //逐个检测并上传文件
        $info = array();
        if(function_exists('finfo_open')){
            $finfo = finfo_open ( FILEINFO_MIME_TYPE );
        }
        // 对上传文件数组信息处理
        $files = $this->dealFiles($files);
        foreach ($files as $key => $file) {
            $file['name']  = strip_tags($file['name']);
            if(!isset($file['key'])){
                $file['key'] = $key;
            }
            //通过扩展获取文件类型,可解决FLASH上传$FILES数组返回文件类型错误的问题
            if(isset($finfo)){
                $file['type'] = finfo_file($finfo, $file['tmp_name']);
            }
            //获取上传文件后缀,允许上传无后缀文件
            $file['ext'] = pathinfo($file['name'], PATHINFO_EXTENSION);
            //文件上传检测
            if (!$this->check($file)){
                continue;
            }
            //获取文件hash
            if($this->hash){
                $file['md5']  = md5_file($file['tmp_name']);
                $file['sha1'] = sha1_file($file['tmp_name']);
            }
            //调用回调函数检测文件是否存在
            $data = call_user_func($this->callback, $file);
            if( $this->callback && $data ){
                if ( file_exists('.'.$data['path'])  ) {
                    $info[$key] = $data;
                    continue;
                }elseif($this->removeTrash){
                    call_user_func($this->removeTrash,$data);//删除垃圾据
                }
            }
            //生成保存文件名
            $savename = $this->getSaveName($file);
            if(false == $savename){
                continue;
            } else {
                $file['savename'] = $savename;
            }
            //检测并创建子目录
            $subpath = $this->getSubPath($file['name']);
            if(false === $subpath){
                continue;
            } else {
                $file['savepath'] = $this->savePath . $subpath;
            }
            //对图像文件进行严格检测
            $ext = strtolower($file['ext']);
            if(in_array($ext, array('gif','jpg','jpeg','bmp','png','swf'))) {
                $imginfo = getimagesize($file['tmp_name']);
                if(empty($imginfo) || ($ext == 'gif' && empty($imginfo['bits']))){
                    $this->error = '非法图像文件!';
                    continue;
                }
            }
            //保存文件 并记录保存成功的文件
            if ($this->save($file,$this->replace)) {
                unset($file['error'], $file['tmp_name']);
                $info[$key] = $file;
            }
        }
        if(isset($finfo)){
            finfo_close($finfo);
        }
        return empty($info) ? false : $info;
    }

    /**
     * 上传图片二进制格式
     * @param 文件信息二进制对象 $file
     */
    public function uploadImgBinary( $fileBinary ){
        if(empty($fileBinary)){
            $this->error = '没有上传的文件!';
            return false;
        }

        //检测上传根目录
        if(!$this->checkRootPath($this->rootPath)){
            return false;
        }

        //检查上传目录
        if(!$this->checkSavePath($this->savePath)){
            return false;
        }

        $imageName = $this->getSaveImgName();
        $fileResult = file_put_contents($this->rootPath.$this->savePath.$imageName, $fileBinary);
        if( empty( $fileResult ) ){
            return $fileResult;
        }
        
        return $this->savePath.$imageName;
    }

    /**
     * 根据取得保存文件名
     * @return string
     */
    private function getSaveImgName() {
        $imgName = md5(uniqid(mt_rand()));
        //文件保存后缀,支持强制更改文件后缀
        $ext = empty($this->saveExt) ? $this->config['saveExt'] : $this->saveExt;
        return $imgName.'.'.$ext;
    }

    /**
     * 转换上传文件数组变量为正确的方式
     * @access private
     * @param array $files  上传的文件变量
     * @return array
     */
    private function dealFiles($files) {
        $fileArray  = array();
        $n          = 0;
        foreach ($files as $key=>$file){
            if(is_array($file['name'])) {
                $keys       =   array_keys($file);
                $count      =   count($file['name']);
                for ($i=0; $i<$count; $i++) {
                    $fileArray[$n]['key'] = $key;
                    foreach ($keys as $_key){
                        $fileArray[$n][$_key] = $file[$_key][$i];
                    }
                    $n++;
                }
            }else{
               $fileArray = $files;
               break;
            }
        }
       return $fileArray;
    }

    /**
     * 检查上传的文件
     * @param array $file 文件信息
     */
    private function check($file) {
        //文件上传失败,捕获错误代码
        if ($file['error']) {
            $this->error($file['error']);
            return false;
        }
        //无效上传
        if (empty($file['name'])){
            $this->error = '未知上传错误!';
            return false;
        }
        //检查是否合法上传
        if (!is_uploaded_file($file['tmp_name'])) {
            $this->error = '非法上传文件!';
            return false;
        }
        //检查文件大小
        if (!$this->checkSize($file['size'])) {
            $this->error = '上传文件大小不符!';
            return false;
        }
        //检查文件Mime类型
        //TODO:FLASH上传的文件获取到的mime类型都为application/octet-stream
        if (!$this->checkMime($file['type'])) {
            $this->error = '上传文件MIME类型不允许!';
            return false;
        }
        //检查文件后缀
        if (!$this->checkExt($file['ext'])) {
            $this->error = '上传文件后缀不允许';
            return false;
        }
        //通过检测
        return true;
    }

    /**
     * 检查文件大小是否合法
     * @param integer $size 数据
     */
    private function checkSize($size) {
        return !($size > $this->maxSize) || (0 == $this->maxSize);
    }
    /**
     * 检查上传的文件MIME类型是否合法
     * @param string $mime 数据
     */
    private function checkMime($mime) {
        return empty($this->config['mimes']) ? true : in_array(strtolower($mime), $this->mimes);
    }
    /**
     * 检查上传的文件后缀是否合法
     * @param string $ext 后缀
     */
    private function checkExt($ext) {
        return empty($this->config['exts']) ? true : in_array(strtolower($ext), $this->exts);
    }
    /**
     * 根据上传文件命名规则取得保存文件名
     * @param string $file 文件信息
     */
    private function getSaveName($file) {
        $rule = $this->saveName;
        if (empty($rule)) { //保持文件名不变
            //解决pathinfo中文文件名BUG
            $filename = substr(pathinfo("_{$file['name']}", PATHINFO_FILENAME), 1);
            $savename = $filename;
        } else if('md5'==$rule){
            $savename = $file['md5'];
        }else if('hash'==$rule){
            $savename = $file['sha1'];
        }else if('md5Time'==$rule){
            $savename = $file['md5'].time();
        }else if('hashTime'==$rule){
            $savename = $file['sha1'].time();
        } else{
            $savename = $this->getName($rule, $file['name']);
            if(empty($savename)){
                $this->error = '文件命名规则错误!';
                return false;
            }
        }
        //文件保存后缀,支持强制更改文件后缀
        $ext = empty($this->config['saveExt']) ? $file['ext'] : $this->saveExt;
        return $savename.'.'.$ext;
    }

    /**
     * 根据指定的规则获取文件或目录名称
     * @param  array  $rule     规则
     * @param  string $filename 原文件名
     * @return string           文件或目录名称
     */
    private function getName($rule, $filename){
        $name = '';
        if(is_array($rule)){ //数组规则
            $func     = $rule[0];
            $param    = (array)$rule[1];
            foreach ($param as &$value) {
               $value = str_replace('__FILE__', $filename, $value);
            }
            $name = call_user_func_array($func, $param);
        } elseif (is_string($rule)){ //字符串规则
            if(function_exists($rule)){
                $name = call_user_func($rule);
            } else {
                $name = $rule;
            }
        }
        return $name;
    }

    /**
     * 获取子目录的名称
     * @param array $file  上传的文件信息
     */
    private function getSubPath($filename) {
        $subpath = '';
        $rule    = $this->subName;
        if ($this->autoSub && !empty($rule)) {
            $subpath = $this->getName($rule, $filename) . '/';

            if(!empty($subpath) && !$this->mkdir($this->savePath . $subpath)){
                return false;
            }
        }
        return $subpath;
    }

    /**
     * 获取错误代码信息
     * @param string $errorNo  错误号
     */
    private function error($errorNo) {
        switch ($errorNo) {
            case 1:
                $this->error = '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值!';
                break;
            case 2:
                $this->error = '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值!';
                break;
            case 3:
                $this->error = '文件只有部分被上传!';
                break;
            case 4:
                $this->error = '没有文件被上传!';
                break;
            case 6:
                $this->error = '找不到临时文件夹!';
                break;
            case 7:
                $this->error = '文件写入失败!';
                break;
            default:
                $this->error = '未知上传错误!';
        }
    }


    /**======================================================**/
    /**
     * 检测上传根目录
     * @param string $rootpath   根目录
     * @return boolean true-检测通过,false-检测失败
     */
    public function checkRootPath($rootpath){
        if(empty($rootpath)){
            $rootpath = $this->config['rootPath'];
        }

        if( stripos($rootpath, ROOT_PATH) === false ){
            $rootpath = ROOT_PATH.ltrim( $rootpath, '.' );
        }

        if(!(is_dir($rootpath) && is_writable($rootpath))){
            $this->error = '上传根目录不存在!请尝试手动创建:'.$rootpath;
            return false;
        }
        $this->rootPath = $rootpath;
        return true;
    }

    /**
     * 检测上传目录
     * @param  string $savepath 上传目录
     * @return boolean          检测结果,true-通过,false-失败
     */
    public function checkSavePath($savepath){
        //检测并创建目录
        if (!$this->mkdir($savepath)) {
            return false;
        } else {
            /* 检测目录是否可写 */
            if (!is_writable($this->rootPath . $savepath)) {
                $this->error = '上传目录 ' . $savepath . ' 不可写!';
                return false;
            } else {
                return true;
            }
        }
    }

    /**
     * 创建目录
     * @param  string $savepath 要创建的穆里
     * @return boolean          创建状态,true-成功,false-失败
     */
    public function mkdir($savepath){
        $dir = $this->rootPath.$savepath;
        if(is_dir($dir)){
            return true;
        }
        if(mkdir($dir, 0777, true)){
            return true;
        } else {
            $this->error = "目录 {$savepath} 创建失败!";
            return false;
        }
    }

    /**
     * 保存指定文件
     * @param  array   $file    保存的文件信息
     * @param  boolean $replace 同名文件是否覆盖
     * @return boolean          保存状态,true-成功,false-失败
     */
    public function save($file, $replace=true) {
        $filename = $this->rootPath.$file['savepath'].$file['savename'];
        //不覆盖同名文件 
        if (!$replace && is_file($filename)) {
            $this->error = '存在同名文件'.$file['savename'];
            return false;
        }
        //移动文件
        if (!move_uploaded_file($file['tmp_name'], $filename)) {
            $this->error = '文件上传保存错误!';
            return false;
        }
        return true;
    }  

}

 

举报
收藏0次 / 评论0
评论(0)
还可以输入 2000 个字符
还可以输入 2000 个字符
取消回复
举报×

还可以输入 264 字符

收藏(0)×