ThinkPHP的Upload.class.php解析
来源:互联网 发布:我的世界清空玩家数据 编辑:程序博客网 时间:2024/05/22 17:44
前面写过一篇文章,也是关于这个的,不过我不满意,所以重新写一遍。
在这里我不会按照这个类逐行解释过程了,那样效率不高,而且也没有什么用,所以这里就先讲一下他的工作原理,再来就是使用的方法了及其过程了。
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,就是下面的格式:
而调用dealFiles之后的$files格式是这样的:
之后会单独处理每隔$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的Upload.class.php解析
- thinkphp view.class.php
- thinkphp Upload
- ThinkPHP 文件上传Upload类的使用
- [php] thinkphp RBAC解析
- 让ThinkPHP的Page.class.php支持简短分页路由
- thinkphp Controller.class.php 控制器类的祖宗分析
- Upload class
- php图片处理类class.upload.php documentation
- PHP多文件上传类--upload.class.php
- [php] thinkphp生僻标签解析
- ThinkPHP数据分页Page.class.php
- thinkPHP内置模板引擎TagLibHtml.class.php
- thinkphp- CronRunBehavior.class.php如何使用?
- Thinkphp 3.2.1 使用CronRunBehavior.class.php
- thinkphp核心源码注释|Think.class.php
- thinkphp核心源码注释|Storage.class.php
- thinkphp核心源码注释|Route.class.php
- 简单看Spring源码--对xml文件解析
- 【Spring学习19】作用域:使用代理<aop:scoped-proxy/>
- linux CentOS学习笔记之文件系统及权限
- 贪心算法之分糖果
- DetachedCriteria用法
- ThinkPHP的Upload.class.php解析
- java 中的JDBC 进行基础的数据库连接
- redis-08-主从复制
- AtCoder
- ssm框架的理解
- Android Glide框架
- Android例子—HttpURLConnection发送POST、GET请求代码示例
- (组队赛E/F Number of Connected Components )UVALive
- 堆 Heap