CodeIgniter 文件上传类 分析

来源:互联网 发布:布登勃洛克一家知乎 编辑:程序博客网 时间:2024/05/12 22:48

上传文件普遍的过程:

  • 一个上传文件用的表单,允许用户选择一个文件并上传它。
  • 当这个表单被提交,该文件被上传到指定的目录。
  • 同时,该文件将被验证是否符合您设定的要求。
  • 一旦文件上传成功,还要返回一个上传成功的确认窗口。
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 FALSEreturn 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 oneif (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;}}

如果上传的是图片则设置文件的一些属性:宽度、高度。

仅作为笔记之用







原创粉丝点击