CodeIgniter 文件上傳類 分析

上傳文件普遍的過程:

  • 一個上傳文件用的表單,允許用戶選擇一個文件並上傳它。
  • 當這個表單被提交,該文件被上傳到指定的目錄。
  • 同時,該文件將被驗證是否符合您設定的要求。
  • 一旦文件上傳成功,還要返回一個上傳成功的確認窗口。
CodeIgniter的文件上傳類首先利用構造函數初始化偏好設置參數:
public function __construct($props = array())
	{
		if (count($props) > 0)
		{
			$this->initialize($props);
		}

		log_message('debug', "Upload Class Initialized");
	}
其中調用了成員方法initialize($config = array()):
public function initialize($config = array())
	{
		$defaults = array(
							'max_size'			=> 0,
							'max_width'			=> 0,
							'max_height'		=> 0,
							'max_filename'		=> 0,
							'allowed_types'		=> "",
							'file_temp'			=> "",
							'file_name'			=> "",
							'orig_name'			=> "",
							'file_type'			=> "",
							'file_size'			=> "",
							'file_ext'			=> "",
							'upload_path'		=> "",
							'overwrite'			=> FALSE,
							'encrypt_name'		=> FALSE,
							'is_image'			=> FALSE,
							'image_width'		=> '',
							'image_height'		=> '',
							'image_type'		=> '',
							'image_size_str'	=> '',
							'error_msg'			=> array(),
							'mimes'				=> array(),
							'remove_spaces'		=> TRUE,
							'xss_clean'			=> FALSE,
							'temp_prefix'		=> "temp_file_",
							'client_name'		=> ''
						);


		foreach ($defaults as $key => $val)
		{
			if (isset($config[$key]))
			{
				$method = 'set_'.$key;
				if (method_exists($this, $method))
				{
					$this->$method($config[$key]);
				}
				else
				{
					$this->$key = $config[$key];
				}
			}
			else
			{
				$this->$key = $val;
			}
		}

		// if a file_name was provided in the config, use it instead of the user input
		// supplied file name for all uploads until initialized again
		$this->_file_name_override = $this->file_name;
	}
該函數利用:
set_upload_path($path):確保$path末尾只有一個反斜槓
set_filename($path, $filename):如果encrypt_name設置爲true,則使用隨機加密的字符串作爲文件名,否則使用$filename.$i.$file_ext作爲文件名
set_image_properties($path = ''):確定圖片的寬度、高度、類型
set_allowed_types($types):設置被允許上傳的文件類型
set_max_filesize($n)、set_max_filename($n)、set_max_width($n)、set_max_height($n)
set_xss_clean($flag = FALSE):使上傳文件通過xss過濾
然後就是執行文件上傳過程,do_upload($field = 'userfile'):
do_upload()首先判斷$_FILES[$field]是否被設置,如果沒有設置,返回false,終止上傳:
if ( ! isset($_FILES[$field]))
		{
			$this->set_error('upload_no_file_selected');
			return FALSE;
		}
然後判斷上傳路徑,判上傳路徑是否爲一個有效的路徑且是否有寫的權限:
if ( ! $this->validate_upload_path())
		{
			// errors will already be set by validate_upload_path() so just return FALSE
			return FALSE;
		}
validate_upload_path()函數代碼如下:
public function validate_upload_path()
	{
		if ($this->upload_path == '')
		{
			$this->set_error('upload_no_filepath');
			return FALSE;
		}

		if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE)
		{
			$this->upload_path = str_replace("\\", "/", realpath($this->upload_path));
		}

		if ( ! @is_dir($this->upload_path))
		{
			$this->set_error('upload_no_filepath');
			return FALSE;
		}

		if ( ! is_really_writable($this->upload_path))
		{
			$this->set_error('upload_not_writable');
			return FALSE;
		}

		$this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/",  $this->upload_path);
		return TRUE;
	}
接着判斷文件是否是通過 HTTP POST 上傳的,is_uploaded_file()
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->set_error('upload_file_exceeds_limit');
					break;
				case 2: // UPLOAD_ERR_FORM_SIZE
					$this->set_error('upload_file_exceeds_form_limit');
					break;
				case 3: // UPLOAD_ERR_PARTIAL
					$this->set_error('upload_file_partial');
					break;
				case 4: // UPLOAD_ERR_NO_FILE
					$this->set_error('upload_no_file_selected');
					break;
				case 6: // UPLOAD_ERR_NO_TMP_DIR
					$this->set_error('upload_no_temp_directory');
					break;
				case 7: // UPLOAD_ERR_CANT_WRITE
					$this->set_error('upload_unable_to_write_file');
					break;
				case 8: // UPLOAD_ERR_EXTENSION
					$this->set_error('upload_stopped_by_extension');
					break;
				default :   $this->set_error('upload_no_file_selected');
					break;
			}

			return FALSE;
		}
如果文件是通過HTTP POST上傳,則設置上傳數據的類變量:
$this->file_temp = $_FILES[$field]['tmp_name'];
$this->file_size = $_FILES[$field]['size'];
$this->_file_mime_type($_FILES[$field]);
$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
$this->file_name = $this->_prep_filename($_FILES[$field]['name']);
$this->file_ext	 = $this->get_extension($this->file_name);
$this->client_name = $this->file_name;
然後判斷文件是否是被允許上傳的類型:
if ( ! $this->is_allowed_filetype())
		{
			$this->set_error('upload_invalid_filetype');
			return FALSE;
		}
is_allowed_filetype($ignore_mime = FALSE)
public function is_allowed_filetype($ignore_mime = FALSE)
	{
		if ($this->allowed_types == '*')
		{
			return TRUE;
		}

		if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
		{
			$this->set_error('upload_no_file_types');
			return FALSE;
		}

		$ext = strtolower(ltrim($this->file_ext, '.'));

		if ( ! in_array($ext, $this->allowed_types))
		{
			return FALSE;
		}

		// Images get some additional checks
		$image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');

		if (in_array($ext, $image_types))
		{
			if (getimagesize($this->file_temp) === FALSE)
			{
				return FALSE;
			}
		}

		if ($ignore_mime === TRUE)
		{
			return TRUE;
		}

		$mime = $this->mimes_types($ext);

		if (is_array($mime))
		{
			if (in_array($this->file_type, $mime, TRUE))
			{
				return TRUE;
			}
		}
		elseif ($mime == $this->file_type)
		{
				return TRUE;
		}

		return FALSE;
	}
接着確定新名稱和類型是否允許:
if ($this->_file_name_override != '')
		{
			$this->file_name = $this->_prep_filename($this->_file_name_override);

			// If no extension was provided in the file_name config item, use the uploaded one
			if (strpos($this->_file_name_override, '.') === FALSE)
			{
				$this->file_name .= $this->file_ext;
			}

			// An extension was provided, lets have it!
			else
			{
				$this->file_ext	 = $this->get_extension($this->_file_name_override);
			}

			if ( ! $this->is_allowed_filetype(TRUE))
			{
				$this->set_error('upload_invalid_filetype');
				return FALSE;
			}
		}
然後將文件大小轉換爲KB:
if ($this->file_size > 0)
		{
			$this->file_size = round($this->file_size/1024, 2);
		}

接着判斷文件大小是否被允許:
if ( ! $this->is_allowed_filesize())
		{
			$this->set_error('upload_invalid_filesize');
			return FALSE;
		}

如果文件是圖片還要判斷圖片的屬性是否被允許,寬度、高度
if ( ! $this->is_allowed_dimensions())
		{
			$this->set_error('upload_invalid_dimensions');
			return FALSE;
		}

接着清楚文件名中的一些特殊字符:<!--、-->、<、>、"、$、=、;、?、/
$this->file_name = $this->clean_file_name($this->file_name);

如果文件名太長,則截斷文件名:
if ($this->max_filename > 0)
		{
			$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
		}

接着刪除空白的文件名
if ($this->remove_spaces == TRUE)
		{
			$this->file_name = preg_replace("/\s+/", "_", $this->file_name);
		}

接着驗證文件名稱:如果存在同名文件,$overwrite設置爲TRUE則覆蓋文件,如果爲FALSE,則在文件名後加數字,如filename1.jpg
$this->orig_name = $this->file_name;

		if ($this->overwrite == FALSE)
		{
			$this->file_name = $this->set_filename($this->upload_path, $this->file_name);

			if ($this->file_name === FALSE)
			{
				return FALSE;
			}
		}

接着使用XSS filter過濾:
if ($this->xss_clean)
		{
			if ($this->do_xss_clean() === FALSE)
			{
				$this->set_error('upload_unable_to_write_file');
				return FALSE;
			}
		}

最後把文件移動到上傳目錄:
if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
		{
			if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
			{
				$this->set_error('upload_destination_error');
				return FALSE;
			}
		}

如果上傳的是圖片則設置文件的一些屬性:寬度、高度。

僅作爲筆記之用







發佈了37 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章