前面写过一篇文章,也是关于这个的,不过我不满意,所以重新写一遍。
在这里我不会按照这个类逐行解释过程了,那样效率不高,而且也没有什么用,所以这里就先讲一下他的工作原理,再来就是使用的方法了及其过程了。
其实原理很简单,就是借用了php的finfo进行文件的处理,再来使用ThinkPHP下的Library下的Upload下的local.class.php进行上传文件路径的管理。
类似就是下面的方式:
剩下的部分就是保证其中各个参数的正确性。
传入的参数有以下这些:
其实传入字符串也可以的,多个值之间使用逗号隔开,至于这个值是干嘛的,自己百度mime。这个其实就是一个判断值,判断之后接受到的值在不在你设置的这个array内部:
/**
* 检查上传的文件MIME类型是否合法
* @param string $mime 数据
*/
private function checkMime($mime) {
return empty($this->config['mimes']) ? true : in_array(strtolower($mime), $this->mimes);
}
作用跟mimes一样,就是起一个判断的作用,文件最大上传大小,好像单位是bit。
/**
* 检查文件大小是否合法
* @param integer $size 数据
*/
private function checkSize($size) {
return !($size > $this->maxSize) || (0 == $this->maxSize);
}
跟mimes一样,也可以输入字符串,用逗号隔开。起判断作用,文件的后缀。
/**
* 检查上传的文件后缀是否合法
* @param string $ext 后缀
*/
private function checkExt($ext) {
return empty($this->config['exts']) ? true : in_array(strtolower($ext), $this->exts);
}
在设置了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中的。
第一个参数是函数名,第二个参数是传入上一个函数的参数对象,上面的是默认设置。
其实就是一个路径,Uploads的文件路径是这样的:
rootPath/savePath/subName/saveName
这样的格式,而且saveName的文件后缀需要满足exts的设置。
这个就是一个超级大坑!!!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;
}
从上面我们可以看出:
这里我为什么说坑呢?原本我想实现一个功能,就是让文件上传时在文件的原始文件名之前添加一段随机数,例如uniqid(),这样文件名的格式就是:
uniqid()原始文件名
去掉添加的这部分就可以得到文件的原始文件名了,可是看看ThinkPHP的处理方式,这样的功能除非修改Upload类否则是实现不了的。因为在类中虽然允许你传入参数,但是在处理多文件上传的情况下,Upload类会以遍历数组的形式遍历上传的文件,如下所示:
$files = $this->dealFiles($files);
foreach ($files as $key => $file)
原本$files的格式是一般的$_FILES,就是下面的格式:
而调用dealFiles之后的$files格式是这样的:
之后会单独处理每隔$file,也就是会针对每个$file单独执行上面的getSaveName,可是在处理的过程中,并没有传入$file!!!这样我TM怎么知道每次处理的文件的文件名,我只能是使用外部函数来创建一个字符串作为文件的保存名。坑不坑,就问你坑不坑!!!
这个其实是在调用驱动时传入的一个参数:
$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’。
就是在最后的返回结果中是否返回一个md5和sha1的参数,这两个参数的产生方式如下,至于这两个参数有什么用,那就见仁见智了。
/* 获取文件hash */
if($this->hash){
$file['md5'] = md5_file($file['tmp_name']);
$file['sha1'] = sha1_file($file['tmp_name']);
}
下面是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);//删除垃圾据
}
}
从上面的replace中我们已经看到,这个是用来做判断的,如果在这里配置了所需驱动的类型,则会优先使用该驱动类型,而不是配置文件中指定的类型。
上传文件的驱动配置,这个我没研究过,所以这里就不乱说了。
这里再额外解释一点,就是我早上写的一篇关于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){
至此,就是我纠结了一天的东西了,花了一整天在啃这玩意,发现库函数其实也不是那么完美啊。