Собираем
информацию
по крупицам
Статьи - Компьютерное

Web - разработка

Как в CodeIgniter 1.7.x исправить баг при загрузке файлов (класс Upload)
01-08-2010
01:07:10

Фреймверк CodeIgniter - замечательная вещь для небольших проектов. И хотя недавно уже вышел предварительный релиз 2.0, многие предпочитают пользоваться старой и проверенной веткой 1.7.x, ибо эта ветка уже излазана вдоль и поперек, и в интернете накоплен обширный опыт ее использования.

 

Однако, в CodeIgniter версии 1.7.1 и 1.7.2 наличествует баг, который не позволяет сделать правильную загрузку файлов на сервер. Причем, данный баг у некоторых разработчиков (в определенных случаях) получается "лечить", а у других - нет. Первые, у кого "получилось" вылечить, обливают говном вторых, рассказывая про криворукость оппонентов. Вторые остаются в непонятках, и продолжают утверждать, что у них как не работала закачка, так и не работает.

 

Давайте теперь разбираться, что за баг такой странный, и как с ним бороться.

 

Внешнее проявление бага: При закачке файла, организованной с помощью класса Upload, метод $this->upload->do_upload() возвращает FALSE, что означает ошибку при загрузке файла. При этом метод $this->upload->display_errors() возвращает строку вида:

 

Mime type "application/x-msdownload"
The filetype you are attempting to upload is not allowed.

 

Данная ошибка возникает если при конфигурировании загрузчика была указана маска:

 

$config['allowed_types']='gif|jpg|png|rar|zip|exe|txt';

 

и пользователь попытался загрузить *.exe файл. Почему так происходит? Ведь в маске указан тип "exe", и пользователь загружает *.exe-файл?

 

В рунете и у буржуев везде встречается такое объяснение: это происходит из-за того, что в файле APPLICATION/config/mimes.php, для типа exe (или для какого-нибудь другого типа, который мы хотим загрузить) не прописаны все возможные сигнатуры. Нужно дописать неизвестную сигнатуру, и загрузка заработает. В нашем случе, для расширения exe в файле mimes.php дана только одна сигнатура:

 

...

'exe' => 'application/octet-stream',

...

 

Для исправления, нужно прописать все возможные сигнатуры:

 

...

'exe' => array('application/octet-stream',

               'application/x-msdownload',

               'application/x-msdos-program'),

...

 

После чего, у некоторых счастливчиков начинает работать загрузка exe-файла.

 

Почему не у всех?

 

А потому, что загрузка еще и зависит от маски. При вот таких масках загрузка начнет работать:

 

'exe'

'exe|txt'

'txt|exe'

'zip|exe|txt'

'zip|exe|txt|gif'

'zip|txt|exe|gif|jpg'

 

А при таких масках загрузка работать всеравно не будет, и останется то же самое сообщение об ошибке:

 

'zip|rar|jpg|gif|txt|exe'

'gif|jpg|png|rar|zip|exe|txt'

'png|gif|rar|zip|exe|txt'

'gif|exe|txt'

'rar|png|exe'

 

Поняли в чем дело? Закономерность впринципе видна, но неоднозначна.

 

Все дело в баге, который появился в CodeIgniter, начиная с версии 1.7.1, и перетек в версию 1.7.2. Баг находится в классе CI_Upload, в методе is_allowed_filetype().

 

Посмотрим на этот код:

 

function is_allowed_filetype()
{
    if (count($this->allowed_types) == 0 OR

        !is_array($this->allowed_types))
    {
        $this->set_error('upload_no_file_types');
        return FALSE;
    }

    $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');

    foreach ($this->allowed_types as $val)
    {
        $mime = $this->mimes_types(strtolower($val));

        // Images get some additional checks
        if (in_array($val, $image_types))
        {
            if (getimagesize($this->file_temp) === FALSE)
                return FALSE;
        }

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

 

Что ж мы видим? Мы видим дополнительную проверку сразу после комментария "// Images get some additional checks", которая срабатывает даже тогда, когда загружаемый файл не принадлежит ни к одному типу картинок. Функция getimagesize() будет обрабатывать exe-файл, когда в строке маски встретит какой-нибудь тип, соответсвующий картинке - gif, jpg, png. Если этот тип прописан до exe, метод is_allowed_filetype()вернет false.

 

Теперь стало понятно, в чем заключается баг. Будем его исправлять путем переопределения класса ядра.

 

Создадим в директории APPLICATION/libraries файл MY_Upload.php. Этот файл перекроет класс CI_Upload. В файле MY_Upload.php разместим следующий код:

 

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

// Расширение класса CI_Upload, служащее для исправления бага CI,
// возникающего при проверке mime-типов

class MY_Upload extends CI_Upload
{

 function MY_Upload($props = array())
 {
  parent::CI_Upload($props);
 }

 function is_allowed_filetype()
 {
  if (count($this->allowed_types) == 0 OR

      ! is_array($this->allowed_types))
  {
   $this->set_error('upload_no_file_types');
   return FALSE;
  }

  $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');

  foreach ($this->allowed_types as $val)
  {
   $mime = $this->mimes_types(strtolower($val));

   // Если известные CI типы указаны в виде массива строк
   if (is_array($mime))
   {
    if (in_array($this->file_type, $mime, TRUE))
    {
     // Если идет работа с изображением, нужна дополнительная проверка
     if (in_array($val, $image_types))
      return $this->image_additional_check($this->file_temp);
     else
      return TRUE;
    }
   }
   else
   {
    // Иначе, известные CI типы указаны в виде строки
    if ($mime == $this->file_type)
    {
     // Если идет работа с изображением, нужна дополнительная проверка
     if (in_array($val, $image_types))
      return $this->image_additional_check($this->file_temp);
     else
      return TRUE;
    }
   }
  }

  return FALSE;
 }


 private function image_additional_check($file_temp)
 {
  if (getimagesize($file_temp) === FALSE)
   return FALSE;
  else
   return TRUE;
 }
}
?>

 

И после этих действий баг должен исчезнуть.

 

Баг внесен в багтрекер CodeIgniter: http://codeigniter.com/bug_tracker/bug/13409, будет ли исправлен в следующих версиях - не знаю.

 

UPD: Через несколько месяцев после написания этой статьи вышел релиз CodeIgniter v.1.7.3, в котором наконец-то данный баг в классе CI_Upload был исправлен. Пользуйтесь версией 1.7.3 или более старшей.

 



К списку "Компьютерное"

Поделиться этой страницей


Статистика


RSS подписка

Подпишитесь на новости сайта по RSS


 WebHamster.Ru
 Домик любопытного хомячка
Яндекс индекс цитирования
Почтовый ящик