Собираем
информацию
по крупицам

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 или более старшей.

 


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

Интересное на сайте


Перевод документации Ext JS 4.2 » Ext JS 4.2. Часть 3.7: Сохранение данных в Модели данных

    Сохранение данных в Модели данных   В настоящий момент мы имеем таблицу с данными пользователя и окно, которое открывается при двой...


Программирование на C++ и Qt » Qt: как бороться с ошибкой "Point size <= 0 (0), must be greater than 0"

Многие, кто делает программы на фреймверке Qt 4.x, может натолкнутся на то, что запущенная программа в какой-то момент начинает сыпать бесконечными пр...


Flash-анимация » Клип клуба "Сакура"

Оборудование: Pentium-200MMX, RAM 32Мб Среда: Flash MX Год: 2002   Этот клип я создавал, преследуя две цели.   Первая цель - разобраться с ...

RSS подписка

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


Красная поляна будет называться роза хутор www.red-glade.com/hrozahutor.html.

Внимание!

На этом сайте разрабатывается программа MyTetra и её родственные проекты.

Доступны к просмотру следующие базы знаний:

База Xintrea (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)

База Rarrugas (1, 2)

База Balas

База YellowRaven

База Yurons

База Lesnik757

База Shandor

База Sirrichar

 

Подробности на странице MyTetra Share.

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