CI框架源码解析二十之文件上传类文件Upload.php

柴泰平
2023-12-01

        最近一直在研究Codeigniter框架,本篇博文为大家简述解析文件上传类文件的实现,以前写过一篇CI框架优化文件上传方法以及实现多文件上传的博文,大家可以放在一起进行对比阅读。CI框架教程2——优化文件上传方法以及实现多文件上传

 上传一个文件通常涉及以下几步:
        Ⅰ 显示一个上传表单,用户选择文件并上传。 
        Ⅱ 当提交表单时,文件将被上传到你指定的目录。 
        Ⅲ 同时,根据你设置的参数对文件进行校验是否允许上传。 
        Ⅳ 上传成功后,向用户显示成功消息。

通过我们阅读文件上传类的源代码,我们可以明确的知道CI框架的文件上传类主要完成了下面四个功能:
        Ⅰ initialize(array $config = array(), $reset = TRUE)类初始化,处理各种上传配置信息。
        Ⅱ do_upload($field = 'userfile')执行文件上传。
        Ⅲ data($index = NULL)返回一个数组,包含你上传的文件的所有信息。
        Ⅳ display_errors($open = '<p>', $close = '</p>')获取错误信息。

下面我们就来一一详细介绍这四个方法。

1、initialize(array $config = array(), $reset = TRUE)

此方法主要就是进行将配置信息进行处理过滤,也就是在控制器中进行文件上传时进行调用的。例如:

    //文件上传目录
    $config['upload_path'] = './uploads/';
    //文件类型
    $config['allowed_types'] = 'gif|jpg|png';
    //文件最大
    $config['max_size'] = '1024';
    //文件宽度
    $config['max_width'] = '1024';
    //文件高度
    $config['max_height'] = '768';
    
    //$this->load->library('upload', $config);
    //使用下面这个方法是要在控制器的构造函数中引入$this->load->library('upload');
    $this->upload->initialize($config);

2、do_upload($field = 'userfile')

根据你设置的参数上传文件。默认情况下上传文件是来自于表单的 userfile 字段,而且表单应该是 "multipart" 类型。

<form method="post" action="some_action" enctype="multipart/form-data" />
如果你想设置你自己的字段,可以将字段名传给 do_upload() 方法:
    $field_name = "some_field_name";
    $this->upload->do_upload($field_name);

3、data($index = NULL)

该方法返回一个数组,包含你上传的文件的所有信息,下面是数组的原型:(具体参数的含义在这里就不解释了,相信大家都可以一眼看明白。)

Array
(
    [file_name] => mypic.jpg
    [file_type] => image/jpeg
    [file_path] => /path/to/your/upload/
    [full_path] => /path/to/your/upload/jpg.jpg
    [raw_name]  => mypic
    [orig_name] => mypic.jpg
    [client_name]   => mypic.jpg
    [file_ext]  => .jpg
    [file_size] => 22.2
    [is_image]  => 1
    [image_width]   => 800
    [image_height]  => 600
    [image_type]    => jpeg
    [image_size_str] => width="800" height="200"
)
我们也可以只要其中的某一项:
    $this->upload->data('file_name');
    // 返回: mypic.jpg

4、display_errors($open = '<p>', $close = '</p>')

         如果 do_upload() 方法返回 FALSE ,可以使用该方法来获取错误信息。该方法返回所有的错误信息,而不是是直接显示出来。默认情况下该方法会将错误信息包在 <p> 标签中,你可以设置你自己的标签:

    $this->upload->display_errors('<b>', '</b>');

        最后按照惯例贴一下CI框架整个文件上传类Upload.php文件的源代码(注释版):

    <?php
    
    /**
     * =======================================
     * Created by Pocket Knife Technology.
     * User: ZhiHua_W
     * Date: 2016/11/15 0042
     * Time: 下午 5:21
     * Project: CodeIgniter框架—源码分析
     * Power: Analysis for Upload.php
     * =======================================
     */
    
    defined('BASEPATH') OR exit('No direct script access allowed');
    
    /**
     * 文件上传类
     */
    class CI_Upload
    {
        //允许上传文件大小的最大值(单位 KB),
        //设置为 0 表示无限制注意:大多数 PHP 会有它们自己的限制值,
        //定义在 php.ini 文件中通常是默认的 2 MB (2048 KB)。
        public $max_size = 0;
        //图片的最大宽度(单位为像素),设置为 0 表示无限制
        public $max_width = 0;
        //图片的最大高度(单位为像素),设置为 0 表示无限制
        public $max_height = 0;
        //图片的最小宽度(单位为像素),设置为 0 表示无限制
        public $min_width = 0;
        //图片的最小高度(单位为像素),设置为 0 表示无限制
        public $min_height = 0;
        //文件名的最大长度,设置为 0 表示无限制
        public $max_filename = 0;
        //当 overwrite 参数设置为 FALSE 时,将会在同名文件的后面加上一个自增的数字这个参数用于设置这个数字的最大值
        public $max_filename_increment = 100;
        //允许上的文件 MIME 类型,通常文件的后缀名可作为 MIME 类型可以是数组,也可以是以管道符(|)分割的字符串
        public $allowed_types = '';
        //临时文件名
        public $file_temp = '';
        //如果设置了,CodeIgniter 将会使用该参数重命名上传的文件设置的文件名后缀
        //必须也要是允许的文件类型如果没有设置后缀,将使用原文件的后缀名
        public $file_name = '';
        //原始的文件名,只有在使用了 encrypt_name 参数时该值才有用
        public $orig_name = '';
        //文件的 MIME 类型
        public $file_type = '';
        //文件大小(单位 kb)
        public $file_size = NULL;
        //文件后缀名,包括句点
        public $file_ext = '';
        //如果设置为 TRUE ,文件后缀名将转换为小写
        public $file_ext_tolower = FALSE;
        //文件上传的位置,必须是可写的,可以是相对路径或绝对路径
        public $upload_path = '';
        //如果设置为 TRUE ,上传的文件如果和已有的文件同名,将会覆盖已存在文件如果设置为 FALSE ,将会在文件名后加上一个数字
        public $overwrite = FALSE;
        //如果设置为 TRUE ,文件名将会转换为一个随机的字符串如果你不希望上传文件的人知道保存后的文件名,这个参数会很有用
        public $encrypt_name = FALSE;
        //文件是否为图片
        public $is_image = FALSE;
        //图片宽度
        public $image_width = NULL;
        //图片高度
        public $image_height = NULL;
        //图片类型(通常是不带句点的文件后缀名)
        public $image_type = '';
        //一个包含了图片宽度和高度的字符串(用于放在 image 标签中)
        public $image_size_str = '';
        //错误信息
        public $error_msg = array();
        //如果设置为 TRUE ,文件名中的所有空格将转换为下划线,推荐这样做
        public $remove_spaces = TRUE;
        //如果设置为 TRUE ,将会在服务端对文件类型进行检测,
        //可以预防代码注入攻击除非不得已,请不要禁用该选项,这将导致安全风险
        public $detect_mime = TRUE;
        //XSS过滤器的旗帜
        public $xss_clean = FALSE;
        //如果设置为 TRUE ,那么带有多个后缀名的文件将会添加一个下划线后缀这样可以避免触发 Apache mod_mime 。
        //如果你的上传目录是公开的,请不要关闭该选项,这将导致安全风险
        public $mod_mime_fix = TRUE;
        //临时名前缀
        public $temp_prefix = 'temp_file_';
        //用户提交过来的文件名,还没有对该文件名做任何处理
        public $client_name = '';
    
        //文件名重写
        protected $_file_name_override = '';
        //MIME类型
        protected $_mimes = array();
        protected $_CI;
    
        /**
         * 构造函数
         * 进行上传配置初始化
         */
        public function __construct($config = array())
        {
            empty($config) OR $this->initialize($config, FALSE);
            $this->_mimes =& get_mimes();
            $this->_CI =& get_instance();
            log_message('info', 'Upload Class Initialized');
        }
    
        /**
         * 类初始化
         * 将配置信息进行处理过滤
         */
        public function initialize(array $config = array(), $reset = TRUE)
        {
            $reflection = new ReflectionClass($this);
            if ($reset === TRUE) {
                $defaults = $reflection->getDefaultProperties();
                foreach (array_keys($defaults) as $key) {
                    if ($key[0] === '_') {
                        continue;
                    }
                    if (isset($config[$key])) {
                        if ($reflection->hasMethod('set_' . $key)) {
                            $this->{'set_' . $key}($config[$key]);
                        } else {
                            $this->$key = $config[$key];
                        }
                    } else {
                        $this->$key = $defaults[$key];
                    }
                }
            } else {
                foreach ($config as $key => &$value) {
                    if ($key[0] !== '_' && $reflection->hasProperty($key)) {
                        if ($reflection->hasMethod('set_' . $key)) {
                            $this->{'set_' . $key}($value);
                        } else {
                            $this->$key = $value;
                        }
                    }
                }
            }
            //如果一个file_name在配置设置,而不是用户输入文件名全部上传到初始化再次使用它
            $this->_file_name_override = $this->file_name;
            return $this;
        }
    
        /**
         * 执行文件上传
         * 这个就是我们在控制器中调用的方法,传递前端Html的input file类型的name值
         */
        public function do_upload($field = 'userfile')
        {
            //判断是否有文件
            if (isset($_FILES[$field])) {
                $_file = $_FILES[$field];
                //字段名称是否包含数组符号?
            } elseif (($c = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $field, $matches)) > 1) {
                $_file = $_FILES;
                for ($i = 0; $i < $c; $i++) {
                    //我们不能跟踪数值迭代,只有完整的字段名称被接受
                    if (($field = trim($matches[0][$i], '[]')) === '' OR !isset($_file[$field])) {
                        $_file = NULL;
                        break;
                    }
                    $_file = $_file[$field];
                }
            }
            //如果没有文件
            if (!isset($_file)) {
                $this->set_error('upload_no_file_selected', 'debug');
                return FALSE;
            }
            //上传路径是否有效?
            if (!$this->validate_upload_path()) {
                //如果无效返回FALSE
                return FALSE;
            }
            //判断是否能够上传的文件,如果不,确定原因为什么。
            if (!is_uploaded_file($_file['tmp_name'])) {
                $error = isset($_file['error']) ? $_file['error'] : 4;
                //各种错误的原因
                switch ($error) {
                    case UPLOAD_ERR_INI_SIZE:
                        $this->set_error('upload_file_exceeds_limit', 'info');
                        break;
                    case UPLOAD_ERR_FORM_SIZE:
                        $this->set_error('upload_file_exceeds_form_limit', 'info');
                        break;
                    case UPLOAD_ERR_PARTIAL:
                        $this->set_error('upload_file_partial', 'debug');
                        break;
                    case UPLOAD_ERR_NO_FILE:
                        $this->set_error('upload_no_file_selected', 'debug');
                        break;
                    case UPLOAD_ERR_NO_TMP_DIR:
                        $this->set_error('upload_no_temp_directory', 'error');
                        break;
                    case UPLOAD_ERR_CANT_WRITE:
                        $this->set_error('upload_unable_to_write_file', 'error');
                        break;
                    case UPLOAD_ERR_EXTENSION:
                        $this->set_error('upload_stopped_by_extension', 'debug');
                        break;
                    default:
                        $this->set_error('upload_no_file_selected', 'debug');
                        break;
                }
                return FALSE;
            }
            //将上传的数据设置为类变量
            $this->file_temp = $_file['tmp_name'];
            $this->file_size = $_file['size'];
            //MIME类型检测
            if ($this->detect_mime !== FALSE) {
                $this->_file_mime_type($_file);
            }
            $this->file_type = preg_replace('/^(.+?);.*$/', '\\1', $this->file_type);
            $this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
            $this->file_name = $this->_prep_filename($_file['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', 'debug');
                return FALSE;
            }
    
            //让我们现在确定新的名称和类型是允许的
            if ($this->_file_name_override !== '') {
                $this->file_name = $this->_prep_filename($this->_file_name_override);
                //如果没有扩展在file_name配置项设置,使用上传一个
                if (strpos($this->_file_name_override, '.') === FALSE) {
                    $this->file_name .= $this->file_ext;
                } else {
                    //提供了一个扩展
                    $this->file_ext = $this->get_extension($this->_file_name_override);
                }
                if (!$this->is_allowed_filetype(TRUE)) {
                    $this->set_error('upload_invalid_filetype', 'debug');
                    return FALSE;
                }
            }
            //转换文件大小的字节
            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', 'info');
                return FALSE;
            }
    
            //判断图像尺寸范围是否是允许的大小
            if (!$this->is_allowed_dimensions()) {
                $this->set_error('upload_invalid_dimensions', 'info');
                return FALSE;
            }
            //检测文件名设置安全
            $this->file_name = $this->_CI->security->sanitize_filename($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);
            }
            //处理文件后缀
            if ($this->file_ext_tolower && ($ext_length = strlen($this->file_ext))) {
                $this->file_name = substr($this->file_name, 0, -$ext_length) . $this->file_ext;
            }
    
            //验证文件名
            //这个函数添加一个数字到结束
            //如果具有相同名称的文件已存在。
            //如果它返回错误有一个问题。
            $this->orig_name = $this->file_name;
            if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name))) {
                return FALSE;
            }
            //运行文件通过XSS攻击过滤器,这有助于防止恶意代码被嵌入在一个文件中。
            //脚本可以很容易地被伪装成图像或其他文件类型。
            if ($this->xss_clean && $this->do_xss_clean() === FALSE) {
                $this->set_error('upload_unable_to_write_file', 'error');
                return FALSE;
            }
    
            //移动文件到最终目的地处理不同的服务器配置,
            //将先尝试复制文件。如果失败,将使用move_uploaded_file()。
            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', 'error');
                    return FALSE;
                }
            }
            //设置最后的图像尺寸,这设置的图像宽度/高度(假设文件是一个图像)。
            $this->set_image_properties($this->upload_path . $this->file_name);
            return TRUE;
        }
    
    
        /**
         * 确定上传数据
         * 返回一个包含所有与上载相关的信息的关联数组,允许开发者在一个数组中轻松访问。
         * 这也就是我们上传完成后通过这个获取到上传完成之后的文件信息
         */
        public function data($index = NULL)
        {
            //下面的各个名称的含义都不介绍了,相信我们大家可以一眼看出来
            $data = array(
                'file_name' => $this->file_name,
                'file_type' => $this->file_type,
                'file_path' => $this->upload_path,
                'full_path' => $this->upload_path . $this->file_name,
                'raw_name' => substr($this->file_name, 0, -strlen($this->file_ext)),
                'orig_name' => $this->orig_name,
                'client_name' => $this->client_name,
                'file_ext' => $this->file_ext,
                'file_size' => $this->file_size,
                'is_image' => $this->is_image(),
                'image_width' => $this->image_width,
                'image_height' => $this->image_height,
                'image_type' => $this->image_type,
                'image_size_str' => $this->image_size_str,
            );
            if (!empty($index)) {
                return isset($data[$index]) ? $data[$index] : NULL;
            }
            return $data;
        }
    
        /**
         * 设置上传路径
         */
        public function set_upload_path($path)
        {
            //在上传文件路径后面接一个斜杠
            $this->upload_path = rtrim($path, '/') . '/';
            return $this;
        }
    
        /**
         * 设置文件名称
         * 此函数以文件名/路径作为输入,并查找具有相同名称的文件的存在性。
         * 如果找到了,它将大量追加到文件名结束避免覆盖已存在的文件。
         */
        public function set_filename($path, $filename)
        {
            if ($this->encrypt_name === TRUE) {
                $filename = md5(uniqid(mt_rand())) . $this->file_ext;
            }
    
            if ($this->overwrite === TRUE OR !file_exists($path . $filename)) {
                return $filename;
            }
    
            $filename = str_replace($this->file_ext, '', $filename);
    
            $new_filename = '';
            for ($i = 1; $i < $this->max_filename_increment; $i++) {
                if (!file_exists($path . $filename . $i . $this->file_ext)) {
                    $new_filename = $filename . $i . $this->file_ext;
                    break;
                }
            }
    
            if ($new_filename === '') {
                $this->set_error('upload_bad_filename', 'debug');
                return FALSE;
            } else {
                return $new_filename;
            }
        }
    
        /**
         * 设置允许上传文件的最大值
         */
        public function set_max_filesize($n)
        {
            $this->max_size = ($n < 0) ? 0 : (int)$n;
            return $this;
        }
    
        /**
         * 设置允许上传文件的最小值
         */
        protected function set_max_size($n)
        {
            return $this->set_max_filesize($n);
        }
    
        /**
         * 设置最大文件名长度
         */
        public function set_max_filename($n)
        {
            $this->max_filename = ($n < 0) ? 0 : (int)$n;
            return $this;
        }
    
        /**
         * 设置最大文件宽度
         */
        public function set_max_width($n)
        {
            $this->max_width = ($n < 0) ? 0 : (int)$n;
            return $this;
        }
    
        /**
         * 设置最大文件高度
         */
        public function set_max_height($n)
        {
            $this->max_height = ($n < 0) ? 0 : (int)$n;
            return $this;
        }
    
        /**
         * 设置最小文件宽度
         */
        public function set_min_width($n)
        {
            $this->min_width = ($n < 0) ? 0 : (int)$n;
            return $this;
        }
    
        /**
         * 设置最小文件高度
         */
        public function set_min_height($n)
        {
            $this->min_height = ($n < 0) ? 0 : (int)$n;
            return $this;
        }
    
        /**
         * 设置允许上传的文件类型
         */
        public function set_allowed_types($types)
        {
            $this->allowed_types = (is_array($types) OR $types === '*')
                ? $types
                : explode('|', $types);
            return $this;
        }
    
        /**
         * 设置图像属性
         */
        public function set_image_properties($path = '')
        {
            if ($this->is_image() && function_exists('getimagesize')) {
                if (FALSE !== ($D = @getimagesize($path))) {
                    $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
                    $this->image_width = $D[0];
                    $this->image_height = $D[1];
                    $this->image_type = isset($types[$D[2]]) ? $types[$D[2]] : 'unknown';
                    $this->image_size_str = $D[3]; // string containing height and width
                }
            }
            return $this;
        }
    
        /**
         * Set XSS Clean
         */
        public function set_xss_clean($flag = FALSE)
        {
            $this->xss_clean = ($flag === TRUE);
            return $this;
        }
    
        /**
         * 验证图像
         * IE有时会返回奇数的MIME类型在上传,
         * 所以在这里我们只是规范JPEG或PNG图片都到同一个文件类型。
         */
        public function is_image()
        {
            $png_mimes = array('image/x-png');
            $jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
    
            if (in_array($this->file_type, $png_mimes)) {
                $this->file_type = 'image/png';
            } elseif (in_array($this->file_type, $jpeg_mimes)) {
                $this->file_type = 'image/jpeg';
            }
    
            $img_mimes = array('image/gif', 'image/jpeg', 'image/png');
    
            return in_array($this->file_type, $img_mimes, TRUE);
        }
    
        /**
         * 验证文件是允许的
         */
        public function is_allowed_filetype($ignore_mime = FALSE)
        {
            if ($this->allowed_types === '*') {
                return TRUE;
            }
    
            if (empty($this->allowed_types) OR !is_array($this->allowed_types)) {
                $this->set_error('upload_no_file_types', 'debug');
                return FALSE;
            }
    
            $ext = strtolower(ltrim($this->file_ext, '.'));
    
            if (!in_array($ext, $this->allowed_types, TRUE)) {
                return FALSE;
            }
    
            if (in_array($ext, array('gif', 'jpg', 'jpeg', 'jpe', 'png'), TRUE) && @getimagesize($this->file_temp) === FALSE) {
                return FALSE;
            }
    
            if ($ignore_mime === TRUE) {
                return TRUE;
            }
    
            if (isset($this->_mimes[$ext])) {
                return is_array($this->_mimes[$ext])
                    ? in_array($this->file_type, $this->_mimes[$ext], TRUE)
                    : ($this->_mimes[$ext] === $this->file_type);
            }
    
            return FALSE;
        }
    
        /**
         * 验证该文件是否在允许的大小范围内
         */
        public function is_allowed_filesize()
        {
            return ($this->max_size === 0 OR $this->max_size > $this->file_size);
        }
    
        /**
         * 验证图像在允许的宽度/高度
         */
        public function is_allowed_dimensions()
        {
            if (!$this->is_image()) {
                return TRUE;
            }
    
            if (function_exists('getimagesize')) {
                $D = @getimagesize($this->file_temp);
    
                if ($this->max_width > 0 && $D[0] > $this->max_width) {
                    return FALSE;
                }
    
                if ($this->max_height > 0 && $D[1] > $this->max_height) {
                    return FALSE;
                }
    
                if ($this->min_width > 0 && $D[0] < $this->min_width) {
                    return FALSE;
                }
    
                if ($this->min_height > 0 && $D[1] < $this->min_height) {
                    return FALSE;
                }
            }
    
            return TRUE;
        }
    
    
        /**
         * 验证文件上传路径
         * 验证它是一个有效的上传路径与适当的权限。
         */
        public function validate_upload_path()
        {
            if ($this->upload_path === '') {
                $this->set_error('upload_no_filepath', 'error');
                return FALSE;
            }
    
            if (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', 'error');
                return FALSE;
            }
    
            if (!is_really_writable($this->upload_path)) {
                $this->set_error('upload_not_writable', 'error');
                return FALSE;
            }
    
            $this->upload_path = preg_replace('/(.+?)\/*$/', '\\1/', $this->upload_path);
            return TRUE;
        }
    
        /**
         * 提取文件扩展名
         */
        public function get_extension($filename)
        {
            $x = explode('.', $filename);
            if (count($x) === 1) {
                return '';
            }
            $ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x);
            return '.' . $ext;
        }
    
        /**
         * 限制文件名长度
         */
        public function limit_filename_length($filename, $length)
        {
            if (strlen($filename) < $length) {
                return $filename;
            }
            $ext = '';
            if (strpos($filename, '.') !== FALSE) {
                $parts = explode('.', $filename);
                $ext = '.' . array_pop($parts);
                $filename = implode('.', $parts);
            }
            return substr($filename, 0, ($length - strlen($ext))) . $ext;
        }
    
        /**
         * 运行文件通过XSS清洁功能
         */
        public function do_xss_clean()
        {
            $file = $this->file_temp;
            if (filesize($file) == 0) {
                return FALSE;
            }
            if (memory_get_usage() && ($memory_limit = ini_get('memory_limit'))) {
                $memory_limit *= 1024 * 1024;
                $memory_limit = number_format(ceil(filesize($file) + $memory_limit), 0, '.', '');
                ini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net
            }
            if (function_exists('getimagesize') && @getimagesize($file) !== FALSE) {
                if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
                {
                    return FALSE; // Couldn't open the file, return FALSE
                }
                $opening_bytes = fread($file, 256);
                fclose($file);
                return !preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes);
            }
            if (($data = @file_get_contents($file)) === FALSE) {
                return FALSE;
            }
            return $this->_CI->security->xss_clean($data, TRUE);
        }
    
        /**
         * 设置错误提示信息
         */
        public function set_error($msg, $log_level = 'error')
        {
            $this->_CI->lang->load('upload');
            is_array($msg) OR $msg = array($msg);
            foreach ($msg as $val) {
                $msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val);
                $this->error_msg[] = $msg;
                log_message($log_level, $msg);
            }
            return $this;
        }
    
        /**
         * 显示错误消息
         */
        public function display_errors($open = '<p>', $close = '</p>')
        {
            return (count($this->error_msg) > 0) ? $open . implode($close . $open, $this->error_msg) . $close : '';
        }
    
        /**
         * 准备文件
         * 防止可能的脚本执行从Apache处理文件的多个扩展名。
         */
        protected function _prep_filename($filename)
        {
            if ($this->mod_mime_fix === FALSE OR $this->allowed_types === '*' OR ($ext_pos = strrpos($filename, '.')) === FALSE) {
                return $filename;
            }
            $ext = substr($filename, $ext_pos);
            $filename = substr($filename, 0, $ext_pos);
            return str_replace('.', '_', $filename) . $ext;
        }
    
        /**
         * 文件MIME类型
         */
        protected function _file_mime_type($file)
        {
            $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
            if (function_exists('finfo_file')) {
                $finfo = @finfo_open(FILEINFO_MIME);
                if (is_resource($finfo)) {
                    $mime = @finfo_file($finfo, $file['tmp_name']);
                    finfo_close($finfo);
                    if (is_string($mime) && preg_match($regexp, $mime, $matches)) {
                        $this->file_type = $matches[1];
                        return;
                    }
                }
            }
            if (DIRECTORY_SEPARATOR !== '\\') {
                $cmd = function_exists('escapeshellarg')
                    ? 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1'
                    : 'file --brief --mime ' . $file['tmp_name'] . ' 2>&1';
    
                if (function_usable('exec')) {
                    $mime = @exec($cmd, $mime, $return_status);
                    if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches)) {
                        $this->file_type = $matches[1];
                        return;
                    }
                }
    
                if (!ini_get('safe_mode') && function_usable('shell_exec')) {
                    $mime = @shell_exec($cmd);
                    if (strlen($mime) > 0) {
                        $mime = explode("\n", trim($mime));
                        if (preg_match($regexp, $mime[(count($mime) - 1)], $matches)) {
                            $this->file_type = $matches[1];
                            return;
                        }
                    }
                }
    
                if (function_usable('popen')) {
                    $proc = @popen($cmd, 'r');
                    if (is_resource($proc)) {
                        $mime = @fread($proc, 512);
                        @pclose($proc);
                        if ($mime !== FALSE) {
                            $mime = explode("\n", trim($mime));
                            if (preg_match($regexp, $mime[(count($mime) - 1)], $matches)) {
                                $this->file_type = $matches[1];
                                return;
                            }
                        }
                    }
                }
            }
            if (function_exists('mime_content_type')) {
                $this->file_type = @mime_content_type($file['tmp_name']);
                if (strlen($this->file_type) > 0) {
                    return;
                }
            }
            $this->file_type = $file['type'];
        }
    }

 类似资料: