ThinkPHP的Upload.class.php解析

前面写过一篇文章,也是关于这个的,不过我不满意,所以重新写一遍。

在这里我不会按照这个类逐行解释过程了,那样效率不高,而且也没有什么用,所以这里就先讲一下他的工作原理,再来就是使用的方法了及其过程了。

Part 1:Upload原理

其实原理很简单,就是借用了php的finfo进行文件的处理,再来使用ThinkPHP下的Library下的Upload下的local.class.php进行上传文件路径的管理。

类似就是下面的方式:

  • finfo:进行上传文件的处理
  • local.class.php:进行上传路径的管理

剩下的部分就是保证其中各个参数的正确性。

Part 2:传入参数处理过程

传入的参数有以下这些:

mimes

  • array类型

其实传入字符串也可以的,多个值之间使用逗号隔开,至于这个值是干嘛的,自己百度mime。这个其实就是一个判断值,判断之后接受到的值在不在你设置的这个array内部:

    /**
     * 检查上传的文件MIME类型是否合法
     * @param string $mime 数据
     */
    private function checkMime($mime) {
        return empty($this->config['mimes']) ? true : in_array(strtolower($mime), $this->mimes);
    }

maxSize

  • 整数类型

作用跟mimes一样,就是起一个判断的作用,文件最大上传大小,好像单位是bit。

    /**
     * 检查文件大小是否合法
     * @param integer $size 数据
     */
    private function checkSize($size) {
        return !($size > $this->maxSize) || (0 == $this->maxSize);
    }

exts

  • array类型

跟mimes一样,也可以输入字符串,用逗号隔开。起判断作用,文件的后缀。

    /**
     * 检查上传的文件后缀是否合法
     * @param string $ext 后缀
     */
    private function checkExt($ext) {
        return empty($this->config['exts']) ? true : in_array(strtolower($ext), $this->exts);
    }

autoSub

  • true或者false类型

在设置了rootPath和savePath之后,还会有子目录,这个子目录的默认格式是下一个参数,这个再说,而这里只是一个标志,表示是否开启子目录。

    /**
     * 获取子目录的名称
     * @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->uploader->mkdir($this->savePath . $subpath)){
                $this->error = $this->uploader->getError();
                return false;
            }
        }
        return $subpath;
    }

这里我备注一下getName函数的作用,就是将$rule数组的第一个值作为函数名,第二个值作为参数,将处理结果返回,如果$rule是字符串格式的话,则也是作为数组函数来处理,只是没有传入参数。可以去搜索一下php的call_user_func_array函数,在getName里面就是调用了这个函数来实现上述功能的。

上面我说的函数都是写在应用下的Common文件夹下的function.php中的。

subName

  • array(‘date’,’Y-m-d’)类型

第一个参数是函数名,第二个参数是传入上一个函数的参数对象,上面的是默认设置。

rootPath

  • ‘./Uploads/’,字符串类型

其实就是一个路径,Uploads的文件路径是这样的:

rootPath/savePath/subName/saveName

这样的格式,而且saveName的文件后缀需要满足exts的设置。

savePath

saveName

  • array(‘uniqid’,”)

这个就是一个超级大坑!!!Upload有专门一段函数来帮助别人设置这个saveName的值,如下所示:

    /**
     * 根据上传文件命名规则取得保存文件名
     * @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 {
            $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;
    }

从上面我们可以看出:

  • saveName为空:文件名保持不变,原杨上传
  • saveName为数组或者字符串形式,则调用getName进行处理,前面我们在autoSub中讲过了getName的作用。

这里我为什么说坑呢?原本我想实现一个功能,就是让文件上传时在文件的原始文件名之前添加一段随机数,例如uniqid(),这样文件名的格式就是:

uniqid()原始文件名

去掉添加的这部分就可以得到文件的原始文件名了,可是看看ThinkPHP的处理方式,这样的功能除非修改Upload类否则是实现不了的。因为在类中虽然允许你传入参数,但是在处理多文件上传的情况下,Upload类会以遍历数组的形式遍历上传的文件,如下所示:

    $files   =  $this->dealFiles($files);
    foreach ($files as $key => $file)

原本$files的格式是一般的$_FILES,就是下面的格式:

ThinkPHP的Upload.class.php解析_第1张图片

而调用dealFiles之后的$files格式是这样的:

ThinkPHP的Upload.class.php解析_第2张图片

之后会单独处理每隔$file,也就是会针对每个$file单独执行上面的getSaveName,可是在处理的过程中,并没有传入$file!!!这样我TM怎么知道每次处理的文件的文件名,我只能是使用外部函数来创建一个字符串作为文件的保存名。坑不坑,就问你坑不坑!!!

saveExt

  • 字符串类型,默认为空,如果有,则只能是一个确定的文件类型的字符串。如’txt’。所有上传的文件最后都会依该文件后缀保存。

replace

  • false或者true类型,是否替换掉同名文件。

这个其实是在调用驱动时传入的一个参数:

$this->uploader->save($file,$this->replace)

而 uploader 则是一个实例化的了ThinkPHP下的Library下的Upload下的local.class.php。

    /**
     * 设置上传驱动
     * @param string $driver 驱动名称
     * @param array $config 驱动配置     
     */
    private function setDriver($driver = null, $config = null){
        $driver = $driver ? : ($this->driver       ? : C('FILE_UPLOAD_TYPE'));
        $config = $config ? : ($this->driverConfig ? : C('UPLOAD_TYPE_CONFIG'));
        $class = strpos($driver,'\\')? $driver : 'Think\\Upload\\Driver\\'.ucfirst(strtolower($driver));
        $this->uploader = new $class($config);
        if(!$this->uploader){
            E("不存在上传驱动:{$name}");
        }
    }

上面的C(‘FILE_UPLOAD_TYPE’))默认是’local’。

hash

  • true或者false类型

就是在最后的返回结果中是否返回一个md5和sha1的参数,这两个参数的产生方式如下,至于这两个参数有什么用,那就见仁见智了。

    /* 获取文件hash */
        if($this->hash){
            $file['md5']  = md5_file($file['tmp_name']);
            $file['sha1'] = sha1_file($file['tmp_name']);
        }

callback

  • false或者字符串类型

下面是upload函数中的一段,专门调用callback的,从中可以看出,callback是用来判断上传文件是否存在的,本来我是希望借用callback来实现我上面希望的功能的,但是注意看,虽然他把$file作为参数传入函数中了,但是返回值的$data却没有再和保存最后结果的$file有任何关系,再一次泪奔。

    /* 调用回调函数检测文件是否存在 */
            $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);//删除垃圾据
                }
            }

driver

  • false或者字符串类型

从上面的replace中我们已经看到,这个是用来做判断的,如果在这里配置了所需驱动的类型,则会优先使用该驱动类型,而不是配置文件中指定的类型。

driverConfig

  • array类型

上传文件的驱动配置,这个我没研究过,所以这里就不乱说了。

Part 3 额外解释

这里再额外解释一点,就是我早上写的一篇关于thinkphp多文件上传实例中为什么要在

$info=$upload->upload(array($_FILES['files']));

中添加这个array($_FILES[‘files’])这样一部分了,先来看upload方法的开始部分:

public function upload($files='') {
        if('' === $files){
            $files  =   $_FILES;
        }
        if(empty($files)){
            $this->error = '没有上传的文件!';
            return false;
        }

如果没有传入参数的话,默认是将$_FILES传入,作为接下来解析的原始数据,可是实际应该传入的参数是$_FILES[‘files’],至于为什么要用数组形式,是因为下面的dealFiles中是将传入参数当成数组形式的,而不是多个数组形式:

private function dealFiles($files) {
        $fileArray  = array();
        $n          = 0;
        foreach ($files as $key=>$file){

至此,就是我纠结了一天的东西了,花了一整天在啃这玩意,发现库函数其实也不是那么完美啊。

你可能感兴趣的:(thinkphp)