436 lines
13 KiB
PHP
436 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* iNewS Project
|
|
*
|
|
* LICENSE
|
|
*
|
|
* http://www.inews.com.cn/license/inews
|
|
*
|
|
* @category iNewS
|
|
* @package ChangeMe
|
|
* @subpackage ChangeMe
|
|
* @copyright Copyright (c) 2008 Zeed Technologies PRC Inc. (http://www.inews.com.cn)
|
|
* @author xSharp ( GTalk: xSharp@gmail.com )
|
|
* @since May 11, 2010
|
|
* @version SVN: $Id$
|
|
*/
|
|
|
|
class Zeed_File_Upload
|
|
{
|
|
protected $_error = array();
|
|
protected $_data = array();
|
|
protected $_queue = array();
|
|
|
|
protected $_options = array(
|
|
'max_size' => 2048000, // 2000k
|
|
'max_width' => 1920,
|
|
'max_height' => 1200,
|
|
'allowed_types' => '', // 未实现
|
|
'upload_path' => '',
|
|
'upload_url' => '',
|
|
'overwrite' => FALSE,
|
|
'encrypt_name' => FALSE, // 未实现
|
|
'mimes' => array(), // 未实现
|
|
'remove_spaces' => TRUE, // 未实现
|
|
'xss_clean' => FALSE, // 未实现
|
|
'archive_date' => '',
|
|
'archive_mime' => FALSE);
|
|
|
|
/**
|
|
* @param Array $config
|
|
*/
|
|
public function __construct($config = array())
|
|
{
|
|
$this->setConfig($config);
|
|
}
|
|
|
|
/**
|
|
* 处理数组方式的上传
|
|
*
|
|
* @param Array $arr
|
|
* @param Array $arr1
|
|
* @param Array $arr2
|
|
* @param Array $arr3
|
|
* @param String $return
|
|
* @return Array
|
|
*/
|
|
public function _prepareArr($errorArr, $nameArr, $tmpArr, $sizeArr, $return = 'c')
|
|
{
|
|
if (is_array($errorArr) and count($errorArr) > 0) {
|
|
foreach ($errorArr as $k => $v) {
|
|
if (is_array($v)) {
|
|
$r = $this->_prepareArr($v, $nameArr . '[' . $k . ']', $tmpArr . '[' . $k . ']', $sizeArr . '[' . $k . ']', $return);
|
|
$str = '$' . $return . '[' . $k . '] = $r;';
|
|
@eval($str);
|
|
} else {
|
|
if ($v == 4)
|
|
continue;
|
|
|
|
$str = '$name = ' . $nameArr . '[' . $k . '];';
|
|
$str .= '$tmp_name = ' . $tmpArr . '[' . $k . '];';
|
|
$str .= '$size = ' . $sizeArr . '[' . $k . '];';
|
|
@eval($str);
|
|
|
|
$ret = $this->_prepare($name, $tmp_name, $size);
|
|
$str = '$' . $return . '[' . $k . '] = $ret;';
|
|
eval($str);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $$return;
|
|
}
|
|
|
|
/**
|
|
* 预处理上传文件
|
|
*
|
|
* @param String $name
|
|
* @param String $tmp_name
|
|
* @param Integer $size
|
|
* @return Array
|
|
*/
|
|
private function _prepare($name, $tmp_name, $size)
|
|
{
|
|
$data = array();
|
|
$data['name'] = $name;
|
|
$data['tmp_name'] = $tmp_name;
|
|
if (file_exists($tmp_name)) {
|
|
$data['type'] = Zeed_File::mime($tmp_name);
|
|
$data['hash'] = md5_file($tmp_name);
|
|
$data['size'] = $size;
|
|
$data['target'] = $this->generateTargetFilename($name, $data['type']);
|
|
$data['target_exist'] = (file_exists($this->generateTargetFilename($name, $data['type']))) ? 1 : 0;
|
|
if ($size > $this->getConfig('max_size')) {
|
|
$data['error'] = true;
|
|
$data['error_msg'] = 'Invalid filesize.';
|
|
} elseif ($this->isImage($data['type'])) {
|
|
$imgProperties = $this->getImageProperties($tmp_name);
|
|
$data = array_merge($data, $imgProperties);
|
|
}
|
|
} else {
|
|
$data['type'] = null;
|
|
$data['hash'] = null;
|
|
$data['size'] = 0;
|
|
}
|
|
|
|
// 加入待处理队列,去除重复(MD5值相同)的文件
|
|
$this->_queue[$data['hash']] = $data;
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* 上传到服务器上后移动到目标目录前的准备工作
|
|
*
|
|
* @param string $field 为空时处理所有
|
|
* @return array 一维或多维数组
|
|
*/
|
|
public function prepare($field = null)
|
|
{
|
|
if (is_null($field)) {
|
|
$fields = array_keys($_FILES);
|
|
$ret = array();
|
|
foreach ($fields as $field) {
|
|
$ret[$field] = $this->prepare($field);
|
|
}
|
|
$this->_data = $ret;
|
|
return $ret;
|
|
}
|
|
|
|
$_FILES_error = $_FILES[$field]['error'];
|
|
if (is_array($_FILES_error)) {
|
|
$this->_data = $this->_prepareArr($_FILES_error, '$_FILES[' . $field . '][name]', '$_FILES[' . $field . '][tmp_name]', '$_FILES[' . $field . '][size]');
|
|
} else {
|
|
$this->_data = $this->_prepare($_FILES[$field]['name'], $_FILES[$field]['tmp_name'], $_FILES[$field]['size']);
|
|
}
|
|
|
|
return $this->_data;
|
|
}
|
|
|
|
/**
|
|
* 处理(从临时目录向目标目录移动)单个文件
|
|
*
|
|
* @param string $hashCode
|
|
* @param string $newName
|
|
* @param boolean $overwrite
|
|
* @return boolean|array
|
|
*/
|
|
public function doUpload($hashCode, $newName = null, $overwrite = false)
|
|
{
|
|
if (isset($this->_queue[$hashCode])) {
|
|
$t = $this->_queue[$hashCode];
|
|
if (! is_null($newName)) {
|
|
$t['target'] = dirname($t['target']) . '/' . $this->cleanFilename($newName);
|
|
}
|
|
if (! $overwrite) {
|
|
$t['target'] = Zeed_File::autoRenameFile($t['target']);
|
|
}
|
|
unset($this->_queue[$hashCode]);
|
|
if ($this->move_uploaded_file($t['tmp_name'], $t['target'])) {
|
|
$t['url'] = str_replace($this->getUploadPath(), "", $t['target']);
|
|
return $t;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function doUploadAll()
|
|
{
|
|
if (count($this->_queue) > 0) {
|
|
$ret = array();
|
|
reset($this->_queue);
|
|
while (list($k, $t) = each($this->_queue)) {
|
|
$ret[$k] = $this->doUpload($k, null, $this->getConfig('overwrite'));
|
|
}
|
|
} else {
|
|
$ret = null;
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* @param string $tmp_name
|
|
* @param string $targetFile
|
|
* @return boolean
|
|
*/
|
|
protected function move_uploaded_file($tmp_name, $targetFile)
|
|
{
|
|
if (! is_dir($targetFolder = dirname($targetFile))) {
|
|
Zeed_Util::mkpath($targetFolder);
|
|
}
|
|
if (! @move_uploaded_file($tmp_name, $targetFile)) {
|
|
if (! @copy($this->file_temp, $this->upload_path . $this->file_name)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 添加到同步队列
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* 生成目标地址
|
|
*
|
|
* @param String $name
|
|
* @param String $mimeType
|
|
* @return String
|
|
*/
|
|
public function generateTargetFilename($name, $mimeType = null)
|
|
{
|
|
$path = $this->getUploadPath();
|
|
if ($this->getConfig('archive_mime')) {
|
|
if (preg_match('/([a-z]{1,})\//', $mimeType, $mm)) {
|
|
$path .= $mm[1] . '/';
|
|
}
|
|
}
|
|
|
|
if ($this->getConfig('archive_date') != '') {
|
|
$path .= '' . date($this->getConfig('archive_date')) . '/';
|
|
}
|
|
return $path . $this->cleanFilename($name);
|
|
}
|
|
|
|
/**
|
|
* @return String
|
|
*/
|
|
public function getUploadPath()
|
|
{
|
|
$path = $this->getConfig('upload_path');
|
|
$path = str_replace("\\", "|", $path);
|
|
$path = str_replace("/", "|", $path);
|
|
$path = str_replace("||", "|", $path);
|
|
$path = str_replace("|", "/", $path);
|
|
$path = (substr($path, - 1) == '/') ? $path : $path . '/';
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* 清理文件名种的非法字符
|
|
*
|
|
* @see Zeed_File::filename_security()
|
|
* @param String $filename
|
|
* @return String
|
|
*/
|
|
public function cleanFilename($filename)
|
|
{
|
|
return Zeed_File::filename_security($filename);
|
|
}
|
|
|
|
/**
|
|
* 根据MimeType判断是否为图片
|
|
* 只处理JPG/PNG/GIF
|
|
*
|
|
* @param String $fileMimeType
|
|
* @return Boolean
|
|
*/
|
|
public function isImage($fileMimeType)
|
|
{
|
|
$png_mimes = array(
|
|
'image/x-png');
|
|
$jpeg_mimes = array(
|
|
'image/jpg',
|
|
'image/jpe',
|
|
'image/jpeg',
|
|
'image/pjpeg');
|
|
|
|
if (in_array($fileMimeType, $png_mimes)) {
|
|
$fileMimeType = 'image/png';
|
|
}
|
|
|
|
if (in_array($fileMimeType, $jpeg_mimes)) {
|
|
$fileMimeType = 'image/jpeg';
|
|
}
|
|
|
|
$img_mimes = array(
|
|
'image/gif',
|
|
'image/jpeg',
|
|
'image/png');
|
|
|
|
return (in_array($fileMimeType, $img_mimes, TRUE)) ? TRUE : FALSE;
|
|
}
|
|
|
|
/**
|
|
* 获取图片文件的宽高等信息.
|
|
* 该函数不判断参数文件是否为图片,如果需要判断,参见UtilUpload::isImage($fileMimeType)
|
|
*
|
|
* @param String $path
|
|
* @return Array|Null
|
|
*/
|
|
public function getImageProperties($path)
|
|
{
|
|
if (function_exists('getimagesize')) {
|
|
$ret = array();
|
|
if (FALSE !== ($D = @getimagesize($path))) {
|
|
$types = array(
|
|
1 => 'gif',
|
|
2 => 'jpeg',
|
|
3 => 'png');
|
|
|
|
$ret['image_width'] = $D['0'];
|
|
$ret['image_height'] = $D['1'];
|
|
$ret['image_type'] = (! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];
|
|
// $ret['image_size_str'] = $D['3']; // string containing height and width
|
|
}
|
|
|
|
if ($ret['image_width'] > $this->getConfig('max_width')) {
|
|
$ret['error'] = true;
|
|
$ret['error_msg'] = 'Invalid dimensions.';
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Set configuration
|
|
*
|
|
* @param Array $config
|
|
* @return UtilUpload
|
|
*/
|
|
public function setConfig($config = array())
|
|
{
|
|
if (is_array($config)) {
|
|
$paramsAllow = array_keys($this->_options);
|
|
while (list($key, $val) = each($config)) {
|
|
if (in_array($key, $paramsAllow)) {
|
|
$this->_options[$key] = $val;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 获取当前的配置值
|
|
*
|
|
* @param String|Array $confIndex
|
|
*/
|
|
public function getConfig($confIndex)
|
|
{
|
|
if (! is_array($confIndex)) {
|
|
if (isset($this->_options[$confIndex])) {
|
|
return $this->_options[$confIndex];
|
|
} else {
|
|
throw new Zeed_Exception('Invalid parameter:<b>' . $confIndex . '</b>');
|
|
}
|
|
}
|
|
|
|
if (is_array($confIndex) and count($confIndex) > 0) {
|
|
$ret = array();
|
|
while (list(, $key) = each($confIndex)) {
|
|
|
|
if (isset($this->_options[$key])) {
|
|
$ret[$key] = $this->_options[$key];
|
|
} else {
|
|
throw new Zeed_Exception('Invalid parameter:<b>' . $key . '</b>');
|
|
}
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs the file through the XSS clean function
|
|
*
|
|
* This prevents people from embedding malicious code in their files.
|
|
* I'm not sure that it won't negatively affect certain files in unexpected ways,
|
|
* but so far I haven't found that it causes trouble.
|
|
*
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
function do_xss_clean()
|
|
{
|
|
require_once (dirname(__FILE__) . '/../misc/XSSClean.php');
|
|
$file = $this->upload_path . $this->file_name;
|
|
|
|
if (filesize($file) == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (($data = @file_get_contents($file)) === FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (! $fp = @fopen($file, 'r+b')) {
|
|
return FALSE;
|
|
}
|
|
|
|
$data = fread($fp, filesize($file));
|
|
$data = XSSClean::instance()->clean($data);
|
|
|
|
flock($fp, LOCK_EX);
|
|
fwrite($fp, $data);
|
|
flock($fp, LOCK_UN);
|
|
fclose($fp);
|
|
}
|
|
|
|
public function getData()
|
|
{
|
|
return $this->_data;
|
|
}
|
|
|
|
public function getQueue()
|
|
{
|
|
return $this->_queue;
|
|
}
|
|
|
|
}
|
|
|
|
// End ^ LF ^ encoding
|