namespace Lib;

class Local{

/**

* 上传文件根目录

* @var string

*/

private $rootPath;

/**

* 本地上传错误信息

* @var string

*/

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

/**

* 构造函数,用于设置上传根路径

*/

public function __construct($config = null){

}

/**

* 检测上传根目录

* @param string $rootpath 根目录

* @return boolean true-检测通过,false-检测失败

*/

public function checkRootPath($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 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;

}

/**

* 创建目录

* @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;

}

}

/**

* 获取最后一次上传错误信息

* @return string 错误信息

*/

public function getError(){

return $this->error;

}

}

class Upload {

/**

* 默认上传配置

* @var array

*/

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, //检测文件是否存在回调,如果存在返回文件信息数组

'driver' => '', // 文件上传驱动

'driverConfig' => array(), // 上传驱动配置

);

/**

* 上传错误信息

* @var string

*/

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

/**

* 上传驱动实例

* @var Object

*/

private $uploader;

/**

* 构造方法,用于构造上传实例

* @param array $config 配置

* @param string $driver 要使用的上传驱动 LOCAL-本地上传驱动,FTP-FTP上传驱动

*/

public function __construct($config = array(), $driver = '', $driverConfig = null){

/* 获取配置 */

$this->config = array_merge($this->config, $config);

/* 设置上传驱动 */

$this->setDriver($driver, $driverConfig);

/* 调整配置,把字符串配置参数转换为数组 */

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);

}

}

/**

* 使用 $this->name 获取配置

* @param string $name 配置名称

* @return multitype 配置值

*/

public function __get($name) {

return $this->config[$name];

}

public function __set($name,$value){

if(isset($this->config[$name])) {

$this->config[$name] = $value;

if($name == 'driverConfig'){

//改变驱动配置后重置上传驱动

//注意:必须选改变驱动然后再改变驱动配置

$this->setDriver();

}

}

}

public function __isset($name){

return isset($this->config[$name]);

}

/**

* 获取最后一次上传错误信息

* @return string 错误信息

*/

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->uploader->checkRootPath($this->rootPath)){

$this->error = $this->uploader->getError();

return false;

}

/* 检查上传目录 */

if(!$this->uploader->checkSavePath($this->savePath)){

$this->error = $this->uploader->getError();

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']);

}

/* 生成保存文件名 */

$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->uploader->save($file,$this->replace)) {

unset($file['error'], $file['tmp_name']);

$info[$key] = $file;

} else {

$this->error = $this->uploader->getError();

}

}

if(isset($finfo)){

finfo_close($finfo);

}

return empty($info) ? false : $info;

}

/**

* 转换上传文件数组变量为正确的方式

* @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

$fileArray[$n]['key'] = $key;

foreach ($keys as $_key){

$fileArray[$n][$_key] = $file[$_key][$i];

}

$n++;

}

}else{

$fileArray = $files;

break;

}

}

return $fileArray;

}

/**

* 设置上传驱动

* @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 Local;

if(!$this->uploader){

E("不存在上传驱动:{$name}");

}

}

/**

* 检查上传的文件

* @param array $file 文件信息

*/

private function check($file) {

/* 文件上传失败,捕获错误代码 */

if ($file['error']) {

$this->error($file['error']);

return false;

}

/* 无效上传 */

if (empty($file['name'])){

$this->error = '未知上传错误!';

}

/* 检查是否合法上传 */

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 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 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;

$file['name']=md5($file['name'].time().rand(99,1000));

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;

}

/**

* 获取子目录的名称

* @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;

}

/**

* 根据指定的规则获取文件或目录名称

* @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;

}

}

一键复制

编辑

Web IDE

原始数据

按行查看

历史

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐