<?php
/**
* 文件上传类Upload.php
*
* @author ihelloworld2010@gmail.com
* @version $Id: Upload.php,v 1.0 2012/06/14 14:47:43 ihelloworld Exp $
* @package system
*
*/
class Gsite_File_Upload {
// 常见的扩展名
const EXT_IMAGE = 'bmp|jpg|jpeg|png|gif';
const EXT_DOC = 'txt|csv|doc|xls|ppt|docx|xlsx|pptx|wps';
const EXT_FLASH = 'swf|fla|flv';
const EXT_MEDIA = 'asf|avi|wmv|mid|mov|mp3|mp4|mpc|mpeg|mpg|rm|rmi|rmvb';
const EXT_COMPRESSION = 'rar|zip|tar|tgz|gz';
// 允许上传的扩展名内置模式
const ALLOW_MODE_DEFAULT = 0;
const ALLOW_MODE_GENERAL = 1;
const ALLOW_MODE_IMAGE = 2;
const ALLOW_MODE_DOC = 3;
const ALLOW_MODE_FLASH = 4;
const ALLOW_MODE_MEDIA = 5;
const ALLOW_MODE_COMP = 6;
// 子目录生成模式
const SUB_DIR_MODE_DEFAULT = 0;
const SUB_DIR_MODE_FULLDATE = 1;
const SUB_DIR_MODE_YEAR = 2;
const SUB_DIR_MODE_YEARMONTH = 3;
const SUB_DIR_MODE_YEARMONTHDAY = 4;
// 文件名生成规则
// time uniqid com_create_guid getFilenameByTime
const FILENAME_RULE_DEFAULT = 0;
const FILENAME_RULE_TIMESTAMP = 1;
const FILENAME_RULE_UNIQID = 2;
const FILENAME_RULE_COM_CREATE_GUID = 3;
const FILENAME_RULE_DATETIME = 4;
// 上传错误代码
const UPLOAD_ERR_BAD_FILENAME = 8;
const UPLOAD_ERR_NO_FILEPATH = 9;
const UPLOAD_ERR_NOT_WRITABLE = 10;
const UPLOAD_ERR_NO_FILE_SELECTED = 11;
const UPLOAD_ERR_INVALID_FILETYPE = 12;
const UPLOAD_ERR_INVALID_FILESIZE = 13;
const UPLOAD_ERR_DESTINATION_ERROR = 14;
// 最大字节限制
private $maxSize = -1;
// 允许的文件扩展名
private $allowExts = array();
// 不允许的文件扩展名
private $notAllowExts = array();
// 文件名存在时是否覆盖
private $overwrite = false;
// 文件名是否删除空格
private $removeSpace = true;
// 子目录生成模式
private $subDirMode = 0;
// 文件名生成规则
private $filenameRule = 0;
// 保存路径
private $savePath = '';
// 当前上传的文件名
private $filename = '';
// 当前上传的文件扩展名
private $fileExt = '';
// 文件大小
private $fileSize = 0;
// 错误代码
private $errorCode = 0;
// 错误信息数组
private $errorMessages = array();
// 过虑文件名中的特殊字符
// 生成文件名
// 创建目录
// 设置允许的文件类型
// 是否为图片文件
public function __construct($config = array()) {
$this->initialize($config);
}
/**
* 参数初始化方法
* @param array $config 配置项数组
* @return void
*/
public function initialize($config = array()) {
$default = array(
'maxSize' => -1,
'allowMode' => self::ALLOW_MODE_DEFAULT,
'notAllowExts' => array(),
'overwrite' => false,
'removeSpace' => true,
'subDirMode' => self::SUB_DIR_MODE_DEFAULT,
'filenameRule' => self::FILENAME_RULE_DEFAULT,
'savePath' => '',
);
foreach($default as $key => $val) {
if (isset($config[$key])) {
$method = 'set' . ucfirst($key);
if (method_exists($this, $method)) {
$this->$method($config[$key]);
} else {
$this->$key = $val;
}
} else {
$this->$key = $val;
}
}
}
/**
* 设置文件最大允许的字节数
* @param int size 字节数
* @return void
*/
public function setMaxSize($size) {
$this->maxSize = (int) $size;
}
/**
* 设置允许的扩展名模式
* @param int $mode 模式编号
* @return void
*/
public function setAllowMode($mode) {
switch((int)$mode) {
case self::ALLOW_MODE_GENERAL :
$this->addAllowExts(self::EXT_IMAGE);
$this->addAllowExts(self::EXT_OFFICE);
$this->addAllowExts(self::EXT_FLASH);
$this->addAllowExts(self::EXT_MEDIA);
$this->addAllowExts(self::EXT_COMPRESSION);
break;
case self::ALLOW_MODE_IMAGE :
$this->addAllowExts(self::EXT_IMAGE);
break;
case self::ALLOW_MODE_DOC :
$this->addAllowExts(self::EXT_DOC);
break;
case self::ALLOW_MODE_FLASH :
$this->addAllowExts(self::EXT_FLASH);
break;
case self::ALLOW_MODE_MEDIA :
$this->addAllowExts(self::EXT_MEDIA);
break;
case self::ALLOW_MODE_COMP :
$this->addAllowExts(self::EXT_COMPRESSION);
break;
case self::ALLOW_MODE_DEFAULT :
default :
$this->clearAllowExts();
}
}
/**
* 设置文件名存在时是否覆盖
* @param bool $overwrite 是否覆盖
* @return void
*/
public function setOverwrite($overwrite) {
$this->overwrite = (bool) $overwrite;
}
/**
* 设置是否清除文件名中的空格
* @param bool $removeSpace 是否清除空格
* @return void
*/
public function setRemoveSpace($removeSpace) {
$this->isRemoveSpace = (bool)$removeSpace;
}
/**
* 设置子目录生成模式
* @param int $mode 模式代码
* @return void
*/
public function setSubDirMode($mode) {
$mode = (int)$mode;
$modes = array(
self::SUB_DIR_MODE_DEFAULT,
self::SUB_DIR_MODE_FULLDATE,
self::SUB_DIR_MODE_YEAR,
self::SUB_DIR_MODE_YEARMONTH,
self::SUB_DIR_MODE_YEARMONTHDAY
);
if (in_array($mode, $modes)) {
$this->subDirMode = $mode;
} else {
$this->subDirMode = self::SUB_DIR_MODE_DEFAULT;
}
}
/**
* 设置文件名生成规则
* @param int $rule 规则代码
* @return void
*/
public function setFilenameRule($rule) {
$rule = (int)$rule;
$rules = array(
self::FILENAME_RULE_DEFAULT,
self::FILENAME_RULE_TIMESTAMP,
self::FILENAME_RULE_UNIQID,
self::FILENAME_RULE_COM_CREATE_GUID,
self::FILENAME_RULE_DATETIME
);
if (in_array($rule, $rules)) {
$this->filenameRule = $rule;
} else {
$this->filenameRule = self::FILENAME_RULE_DEFAULT;
}
}
/**
* 设置保存路径
* @param string $path 路径
* @return void
*/
public function setSavePath($path) {
$this->savePath = rtrim($path, '/') . '/';
}
/**
* 设置文件名
* @param string $path 路径
* @param string $filename 文件名
* @return void
*/
public function setFilename($path, $filename) {
if (!file_exists($path . $filename)) {
return $filename;
}
$filename = str_replace('.' . $this->fileExt, '', $filename);
$newFilename = '';
for($i = 1; $i <= 10000000; $i++) {
if (!file_exists($path . $filename . $i . '.' . $this->fileExt)) {
$newFilename = $filename . $i . '.' . $this->fileExt;
break;
}
}
/*
$newFilename = $this->getFilenameByTime() . '.' . $this->fileExt;;
*/
if (strlen($newFilename) === 0) {
$this->setError(self::UPLOAD_ERR_BAD_FILENAME);
return false;
} else {
return $newFilename;
}
}
/**
* 添加允许的扩展名
* @params string/array $types 扩展名
* @return void
*/
public function addAllowExts($types) {
$exts = null;
if (!is_array($types)) {
$exts = explode('|', trim($types));
} else {
$exts = $types;
}
foreach ($exts as $ext) {
if (strlen($ext)) {
$this->allowExts[] = $ext;
}
}
}
/**
* 对充许的扩展名数组清空
* @param void
* @return void
*/
public function clearAllowExts() {
$this->allowExts = array();
}
/**
* 设置不允许的扩展名
* @params string/array $types 扩展名
* @return void
*/
public function setNotAllowExts($types) {
$this->addNotAllowExts($types);
}
/**
* 添加不允许的扩展名
* @params string/array $types 扩展名
* @return void
*/
public function addNotAllowExts($types) {
$exts = null;
if (!is_array($types)) {
$exts = explode('|', trim($types));
} else {
$exts = $types;
}
foreach ($exts as $ext) {
if (strlen($ext)) {
$this->notAllowExts[] = $ext;
}
}
}
/**
* 对不充许的扩展名数组清空
* @param void
* @return void
*/
public function clearNotAllowExts() {
$this->notAllowExts = array();
}
/**
* 设置错误代码
* @param int $code 错误代码
* @return void
*/
public function setError($code) {
$this->errorCode = $code;
}
/**
* 获取子目录路径
* @param void
* @return string
*/
public function getSubDirPath() {
$path = null;
switch ($this->subDirMode) {
case self::SUB_DIR_MODE_FULLDATE :
$path = date('Ymd') . '/';
break;
case self::SUB_DIR_MODE_YEAR :
$path = date('Y') . '/';
break;
case self::SUB_DIR_MODE_YEARMONTH :
$path = date('Y') . '/' . date('m') . '/';
break;
case self::SUB_DIR_MODE_YEARMONTHDAY :
$path = date('Y') . '/' . date('m') . '/' . date('d') . '/';
break;
default :
$path = '';
}
return $path;
}
/**
* 获取错误信息
* @param void
* @return string
*/
public function getErrorMessage() {
if (empty($this->errorMessages)) {
$this->errorMessages = array(
// 0 => '没有错误发生,文件上传成功。',
1 => '上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。',
2 => '上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。',
3 => '文件只有部分被上传。',
4 => '没有文件被上传。',
6 => '找不到临时文件夹。',
7 => '文件写入失败。',
8 => '错误的文件名。',
9 => '无效的上传目录。',
10 => '上传目录不可写。',
11 => '没有选择文件。',
12 => !empty($this->allowExts) ? sprintf('文件类型只能是%s的扩展名。', implode(',', $this->allowExts)) : sprintf('文件类型只能是除%s之外的扩展名。', implode(',', $this->notAllowExts)),
13 => sprintf('文件大小超出限制, 最大%sK。', round($this->maxSize / 1024, 2)),
14 => '文件保存失败。'
);
}
return $this->errorMessages[$this->errorCode];
}
/**
* 上传文件
* @param string $field 客户端控件名
* @param string $savePath 保存路径
* @param string $filename 文件名
* @return bool/string
*/
public function upload($field, $savePath = null, $filename = null) {
// 检查$_FILES数组是否为空
if (!isset($_FILES[$field])) {
$this->setError(self::UPLOAD_ERR_NO_FILE_SELECTED);
return false;
}
if (!is_uploaded_file($_FILES[$field]['tmp_name'])) {// 上传错误
$error = (!isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
switch($error) {
case 1: // UPLOAD_ERR_INI_SIZE
$this->setError(UPLOAD_ERR_INI_SIZE);
break;
case 2: // UPLOAD_ERR_FORM_SIZE
$this->setError(UPLOAD_ERR_FORM_SIZE);
break;
case 3: // UPLOAD_ERR_PARTIAL
$this->setError(UPLOAD_ERR_PARTIAL);
break;
case 4: // UPLOAD_ERR_NO_FILE
$this->setError(UPLOAD_ERR_NO_FILE);
break;
case 6: // UPLOAD_ERR_NO_TMP_DIR
$this->setError(UPLOAD_ERR_NO_TMP_DIR);
break;
case 7: // UPLOAD_ERR_CANT_WRITE
$this->setError(UPLOAD_ERR_CANT_WRITE);
break;
default :
$this->setError(self::UPLOAD_ERR_NO_FILE_SELECTED);
break;
}
return false;
}
$this->fileSize = $_FILES[$field]['size'];
$this->fileExt = $this->getExt($_FILES[$field]['name']);
if (!$this->isAllowedFileExt()) {// 检查扩展名
$this->setError(self::UPLOAD_ERR_INVALID_FILETYPE);
return false;
}
if (!$this->isAllowedFilesize()) {// 检查文件大小
$this->setError(self::UPLOAD_ERR_INVALID_FILESIZE);
return false;
}
if (strlen($savePath)) {
$this->setSavePath($savePath);
}
// 获取子目录路径
$subDirPath = $this->getSubDirPath();
$this->savePath = $subDirPath ? $this->savePath . $subDirPath : $this->savePath;
if (!$this->validateUploadPath($this->savePath)) {// 验证上传目录
return false;
}
if (strlen($filename) === 0) {// 生成文件名
$this->filename = $this->generateFilename($field);
} else {
$this->filename = $filename;
}
if ($this->removeSpace) {// 转换空格
$this->filename = preg_replace('/\s+/', '_', $this->filename);
}
if ($this->overwrite == false) {// 不覆盖, 生成类似的文件名
$this->filename = $this->setFilename($this->savePath, $this->filename);
if (!$this->filename) {
return false;
}
}
// 保存文件
if (!@copy($_FILES[$field]['tmp_name'], $this->savePath . $this->filename)) {
if (!@move_uploaded_file($_FILES[$field]['tmp_name'], $this->savePath . $this->filename)) {
$this->setError(self::UPLOAD_ERR_DESTINATION_ERROR);
return false;
}
}
return $this->savePath . $this->filename;
}
/**
* 验证上传路径是否合法
* @param void
* @return bool
*/
private function validateUploadPath() {
if ($this->savePath == '') {
$this->setError(self::UPLOAD_ERR_NO_FILEPATH);
return false;
}
if (@realpath($this->savePath) !== false) {
$this->savePath = str_replace('\\', '/', realpath($this->savePath)) . '/';
}
if (!is_dir($this->savePath)) {
if (!$this->mkdir($this->savePath)) {
$this->setError(self::UPLOAD_ERR_NO_FILEPATH);
return false;
}
}
if (!$this->isReallyWritable($this->savePath)) {
$this->setError(self::UPLOAD_ERR_NOT_WRITABLE);
return false;
}
// $this->savePath = preg_replace('/(.+?)\/*$/', '\\1', $this->savePath);
return true;
}
/**
* 创建目录
* @param string $path 路径
* @return bool
*/
private function mkdir($path) {
if (is_dir($path)) {
return true;
}
/*
$paths = explode('/', $path);
$currentPath = '';
foreach ($paths as $dir) {
if (strlen($dir) === 0) {
continue;
}
$currentPath = strlen($currentPath) ? $currentPath . '/' . $dir : $dir;
if (is_dir($currentPath)) {
continue;
}
if (!mkdir($currentPath, 0777)) {
return false;
}
}
return true;
*/
return @mkdir($path, 0777, true);
}
/**
* 检查目录或文件是否可写
* @param string $file 目录路径或文件全路径
* @return bool
*/
private function isReallyWritable($file) {
// If we're on a Unix server with safe_mode off we call is_writable
if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == false) {
return is_writable($file);
}
// For windows servers and safe_mode "on" installations we'll actually
// write a file then read it. Bah...
if (is_dir($file)) {
$file = rtrim($file, '/') . '/' . md5(rand(1, 100));
if (($fp = @fopen($file, 'ab')) === false) {
return false;
}
fclose($fp);
@chmod($file, 0777);
@unlink($file);
return true;
} elseif (($fp = @fopen($file, 'ab')) === false) {
return false;
}
fclose($fp);
return true;
}
/**
* 将文件名的空格替换为下划线
* @param string $filename 文件名
* @return string
*/
private function removeSpaces($filename) {
return preg_replace('/\s+', '_', $filename);
}
/**
* 检查扩展名是否允许
* @param void
* @return bool
*/
private function isAllowedFileExt() {
if (!empty($this->allowExts)) {
return in_array($this->fileExt, $this->allowExts);
} else if (!empty($this->notAllowExts)) {// 当没有设置允许时,检查不允许列表
return !in_array($this->fileExt, $this->notAllowExts);
} else {// 没有设置允许,同时没有设置不允许,表示类型都不限
return true;
}
}
/**
* 检查文件大小是否超出限制
* @param void
* @return bool
*/
private function isAllowedFilesize() {
if ($this->maxSize > 0 && $this->fileSize > $this->maxSize) {
return false;
}
return true;
}
/**
* 获取扩展名
* @param string $filename 文件名
* @return string
*/
private function getExt($filename) {
$array = explode('.', $filename);
return $array ? strtolower(end($array)) : '';
}
/**
* 根据生成规则生成文件名
* @param string $field 客户端控件名
* @return string
*/
private function generateFilename($field) {
$filename = null;
switch($this->filenameRule) {
case self::FILENAME_RULE_TIMESTAMP :
$filename = time();
break;
case self::FILENAME_RULE_UNIQID :
$filename = uniqid();
break;
case self::FILENAME_RULE_COM_CREATE_GUID :
$filename = com_create_guid();
break;
case self::FILENAME_RULE_DATETIME :
$filename = $this->getFilenameByTime();
break;
default :
$filename = str_replace('.' . $this->fileExt, '', $_FILES[$field]['name']);
}
return $filename . '.' . $this->fileExt;
}
/**
* 根据时间,随机数生成文件名
* @param void
* @return string
*/
private function getFilenameByTime() {
$rand = mt_rand(0, 1000);
return date('YmdHis') . str_pad($rand, 4, STR_PAD_LEFT);
}
}
使用示例:
<?php
header('content-type:text/html; charset=utf-8');
require_once('FirePHPCore/FirePHP.class.php');
$firephp = FirePHP::getInstance(true);
$firephp->log($_POST, 'post');
$rootPath = dirname(__FILE__);
require_once 'Upload.php';
$config = array(
'maxSize' => 1024,
'allowMode' => Gsite_File_Upload::ALLOW_MODE_DOC,
//'notAllowExts' => 'php',
'overwrite' => false,
'subDirMode' => Gsite_File_Upload::SUB_DIR_MODE_YEARMONTHDAY,
'filenameRule' => Gsite_File_Upload::FILENAME_RULE_DEFAULT,
'savePath' => $rootPath . '/upload'
);
$upload = new Gsite_File_Upload($config);
$result = $upload->upload('fileToUpload');
if (!$result) {
echo json_encode(array('error' => '上传失败: ' . $upload->getErrorMessage()));
} else {
echo json_encode(array('error' => '', 'msg' => '上传成功 ' . $result));
}
//$firephp = FirePHP::getInstance(true);
//$firephp->log($str, 'debug');