За последние 24 часа нас посетили 18129 программистов и 1613 роботов. Сейчас ищут 1880 программистов ...

Класс загрузки файлов на сервер

Тема в разделе "Решения, алгоритмы", создана пользователем shreck, 27 фев 2008.

  1. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    Господа!
    Написал класс загрузки файлов на сервер. Готов выслушать аргументированную критику.
    ЗЫ: не нужно говорить что изобрел велосипед.

    updated

    PHP:
    1.  
    2. <?php
    3.   class fileloader
    4.   {            
    5.     private $fileinfo = null;
    6.    
    7.     /**
    8.      *  @var types    
    9.      *  по умолчанию беруться только 'gif', 'jpg', 'png'
    10.      */        
    11.     private $types = null;
    12.    
    13.     private $uploadErrors = array(
    14.                                   UPLOAD_ERR_OK => 'Файл(ы) загружен(ы).',
    15.                                   UPLOAD_ERR_INI_SIZE => 'Превышен допустимый размер файла(ов) разрешенный настройками php.',
    16.                                   UPLOAD_ERR_FORM_SIZE => 'Превышен допустимый размер файла(ов) (форма).',
    17.                                   UPLOAD_ERR_PARTIAL => 'Файл(ы) был загружен частично.',
    18.                                   UPLOAD_ERR_NO_FILE => 'Файл(ы) не был(и) загружен(ы)',
    19.                                   UPLOAD_ERR_NO_TMP_DIR => 'Не задана временная директория.',
    20.                                   UPLOAD_ERR_CANT_WRITE => 'Ошибка записи файла(ов) на диск.',
    21.                                   UPLOAD_ERR_EXTENSION => 'Неверный формат файла.',
    22.                                 );    
    23.     private $path = null;
    24.    
    25.     // Настройки
    26.     private $encodeFileName = false;        
    27.    
    28.     private $multi = null;
    29.     private $errors = array();
    30.        
    31.     /**
    32.      *  @var max_file_size    
    33.      *  по умолчанию береться значение из настроек сервера
    34.      */        
    35.     private $max_file_size = null;        
    36.    
    37.     public function __construct($upldbox) {
    38.       $this->fileinfo = $_FILES[$upldbox];
    39.       $this->multi = (is_array($this->fileinfo['name'])) ? true : false;
    40.       $this->max_file_size = $this->_sizeBytes(ini_get('upload_max_filesize'));
    41.       $this->types = array('gif', 'jpg', 'png');                            
    42.     }
    43.    
    44.     public function isError() {
    45.       return count($this->errors) ? true : false;
    46.     }
    47.    
    48.     public function getErrors() {
    49.       foreach($this->errors as $e) {
    50.         print '<font color="#ff0000">'.$e.'</font><br>';        
    51.       }
    52.     }
    53.    
    54.     //Главный метод
    55.     public function process() {
    56.    
    57.       if(!$this->_isWritable()) {
    58.         Throw new Exception('Директория не существует или защищена от записи');
    59.       }
    60.        
    61.       if($this->multi) {            
    62.          
    63.           for($i = 0, $k = 0; $i < count($this->fileinfo['name']); $i++) {
    64.             if($this->_noFile($i)) $k++;
    65.           }
    66.                    
    67.           if($k == count($this->fileinfo['name'])) $this->errors[] = 'Нет ни одного файла для загрузки';        
    68.          
    69.           for($i = 0; $i < count($this->fileinfo['name']); $i++) {
    70.                    
    71.             if(empty($this->fileinfo['name'][$i]) and $this->fileinfo['size'][$i] == 0) continue;                                                                                                              
    72.                                                                                    
    73.             $this->fileinfo['name'][$i] = strtolower($this->fileinfo['name'][$i]);            
    74.            
    75.             if(!$this->_checkError($i)) {
    76.               $this->errors[] = $this->_getError($this->_Errno($i));
    77.               break;              
    78.             }                        
    79.            
    80.             if(!$this->_checkSize($i)) {            
    81.               $this->errors[] = 'Превышен допустимый размер файла '.$this->fileinfo['name'][$i].' (max = '.$this->max_file_size.' байт )';                            
    82.             }                                                                            
    83.  
    84.             if(!$this->_checkType($i)) {
    85.               $this->errors[] = 'Тип файла '.$this->fileinfo['name'][$i].' не поддерживается';                                        
    86.             }                                                                                                      
    87.           }//end for
    88.          
    89.           // Проверяем ошибки          
    90.           if(count($this->errors)) {
    91.             Throw new Exception($this->getErrors());
    92.           }
    93.                                                                              
    94.           for($i = 0; $i < count($this->fileinfo['name']); $i++) {
    95.             $tmpname = $this->fileinfo['tmp_name'][$i];
    96.            
    97.             if($this->encodeFileName) {
    98.                 $pinfo = pathinfo($this->fileinfo['name'][$i]);
    99.                 $newname = md5($pinfo['filename']).'.'.$pinfo['extension'];
    100.             } else {
    101.                 $newname = basename($this->fileinfo['name'][$i]);
    102.             }    
    103.            
    104.             if(!(is_uploaded_file($tmpname) and move_uploaded_file($tmpname, $this->path.'/'.$newname))) {
    105.                $this->errors[] = 'Ошибка загрузки';
    106.                continue;                
    107.             }                                          
    108.           }//end for                
    109.       } else {
    110.           $this->fileinfo['name'] = strtolower($this->fileinfo['name']);                    
    111.          
    112.           if($this->_noFile()) {
    113.             Throw new Exception('Нет файлов для загрузки');
    114.           }
    115.                        
    116.           if(!$this->_checkError()) {
    117.             Throw new Exception($this->_getError($this->_Errno()));
    118.           }
    119.           if(!$this->_checkSize()) {
    120.             Throw new Exception('Превышен допустимый размер файла (max = '.$this->max_file_size.' байт )');
    121.           }                          
    122.           if(!$this->_checkType()) {
    123.             Throw new Exception('Тип файла не поддерживается');
    124.           }        
    125.              
    126.           $tmpname = $this->fileinfo['tmp_name'];
    127.                
    128.           if($this->encodeFileName) {
    129.               $pinfo = pathinfo($this->fileinfo['name']);
    130.               $newname = md5($pinfo['filename']).'.'.$pinfo['extension'];
    131.           } else {
    132.               $newname = basename($this->fileinfo['name']);
    133.           }                        
    134.                                    
    135.           return (is_uploaded_file($tmpname) and move_uploaded_file($tmpname, $this->path.'/'.$newname)) ? true : false;                    
    136.       }                                      
    137.     }        
    138.        
    139.    
    140.     private function __set($index, $value) {            
    141.       if(property_exists($this, $index)) {
    142.         switch($index) {
    143.           case 'types':
    144.             if(is_array($value)) {
    145.               $this->types = $value;
    146.             }
    147.           break;
    148.           case 'path':
    149.             if(is_dir($value)) {
    150.               $this->path = $value;
    151.             } else {
    152.               $this->path = null;
    153.             }
    154.           break;
    155.           case 'max_file_size':
    156.             if($value >= $this->_sizeBytes(ini_get('upload_max_filesize'))) {
    157.               $this->max_file_size = $this->_sizeBytes(ini_get('upload_max_filesize'));
    158.             } else {
    159.               $this->max_file_size = $value;
    160.             }
    161.           break;
    162.           case 'encodeFileName':
    163.             $this->encodeFileName = $value;            
    164.           break;          
    165.         }
    166.       } else {
    167.         return false;
    168.       }                
    169.     }
    170.    
    171.     private function __get($index) {
    172.       if(property_exists($this, $index)) {
    173.         return $this->$index;
    174.       }
    175.     }
    176.        
    177.     private function _getError($no) {
    178.       return $this->uploadErrors[$no];
    179.     }  
    180.    
    181.     // 'bmp', 'gif', 'png', 'jpg', 'jpeg', 'pdf', 'zip', 'rar', 'gz', 'bz', 'bz2', 'doc', 'swf'
    182.     private function _getFileType($type) {
    183.       $ret = null;
    184.       switch($type) {
    185.         case 'image/jpeg': $ret = 'jpg'; break;        
    186.         case 'image/gif': $ret = 'gif'; break;
    187.         case 'image/png': $ret = 'png'; break;
    188.         case 'image/bmp': $ret = 'bmp'; break;
    189.         case 'image/x-windows-bmp': $ret = 'bmp'; break;           
    190.         case 'application/pdf': $ret = 'pdf'; break;
    191.         case 'application/zip': $ret = 'zip'; break;
    192.         case 'application/rar': $ret = 'rar'; break;
    193.         case 'application/x-gzip': $ret = 'gz'; break;
    194.         case 'application/x-bzip': $ret = 'bz'; break;
    195.         case 'application/x-bzip2': $ret = 'bz2'; break;
    196.         case 'application/msword': $ret = 'doc'; break;
    197.         case 'application/x-shockwave-flash': $ret = 'swf'; break;                                                                     
    198.       }            
    199.       return $ret;
    200.     }            
    201.    
    202.     private function _checkError($num = -1) {
    203.       return ($num == -1) ?
    204.         ($this->fileinfo['error'] == 0) ? true : false
    205.                           :
    206.         ($this->fileinfo['error'][$num] == 0) ? true : false;                                  
    207.     }
    208.    
    209.     private function _Errno($num = -1) {
    210.       return ($num == -1) ? $this->fileinfo['error'] : $this->fileinfo['error'][$num];            
    211.     }
    212.    
    213.     private function _sizeBytes($val) {
    214.         $val = trim($val);
    215.         $last = strtolower($val{strlen($val)-1});
    216.         switch($last) {            
    217.             case 'g':
    218.                 $val *= 1024;
    219.             case 'm':
    220.                 $val *= 1024;
    221.             case 'k':
    222.                 $val *= 1024;
    223.         }
    224.    
    225.         return $val;
    226.     }    
    227.    
    228.     private function _isWritable() {
    229.       return is_writable($this->path) ? true : false;
    230.     }
    231.        
    232.    
    233.     private function _checkType($num = -1) {      
    234.       return ($num == -1) ?
    235.         (in_array($this->_getFileType($this->fileinfo['type']), $this->types)) ? true : false
    236.                           :
    237.         (in_array($this->_getFileType($this->fileinfo['type'][$num]), $this->types)) ? true : false;          
    238.     }    
    239.    
    240.     private function _checkSize($num = -1) {
    241.       return ($num == -1) ?
    242.         ($this->fileinfo['size'] < $this->_sizeBytes(ini_get('upload_max_filesize'))
    243.           and
    244.         $this->fileinfo['size'] < $this->max_file_size        
    245.               ) ? true : false
    246.                           :        
    247.         ($this->fileinfo['size'][$num] < $this->_sizeBytes(ini_get('upload_max_filesize'))
    248.           and
    249.         $this->fileinfo['size'][$num] < $this->max_file_size
    250.           and
    251.         $this->fileinfo['size'][$num] > 0          
    252.               ) ? true : false;                                                                      
    253.     }
    254.    
    255.     private function _noFile($num = -1) {
    256.       return ($num == -1) ?
    257.         ($this->fileinfo['error'] == 4) ? true : false
    258.                           :
    259.         ($this->fileinfo['error'][$num] == 4) ? true : false;
    260.     }    
    261.        
    262.    
    263.     private function __toString() {
    264.       return nl2br(print_r($this->fileinfo, true));
    265.     }        
    266.   }
    267. ?>
    268.  
     
  2. Anonymous

    Anonymous Guest

    Откинув мелкие придирки, больше связанные с моими личными предпочтениями - вполне достойно.
     
  3. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    не поделишься?
     
  4. Hawk

    Hawk Активный пользователь

    С нами с:
    30 авг 2007
    Сообщения:
    201
    Симпатии:
    0
    Адрес:
    Беларусь
    Помоему хорошая работа, подчеркнул для себя интересные моменты... Вот бы почаще люди скидывали бы свои работы чтобы другие могли учиться;)
     
  5. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    теперь еще тест и ним и ссылку где посмотреть.
     
  6. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    http://positive.stylehost.info/fileupl/
     
  7. Elkaz

    Elkaz Старожил
    Команда форума Модератор

    С нами с:
    26 июн 2006
    Сообщения:
    3.373
    Симпатии:
    0
    Адрес:
    Баку, Азербайджан
    shreck
    Проверка на расширение что-то не правильно работает.
    Создал в РНР Expert Editor файл
    PHP:
    1. <?php echo "<script>alert('Hacked');</script>"; ?>
    И сохранил как js.jpg.
    Твой класс его принял :) Правда не знаю, загрузил или нет, но:
    Все загружено
     
  8. +Sten+

    +Sten+ Активный пользователь

    С нами с:
    27 авг 2007
    Сообщения:
    978
    Симпатии:
    0
    Elkaz
    А почему "не работает"? Проверка ведь на "расширение", а не на тип файла.
     
  9. lexa

    lexa Активный пользователь

    С нами с:
    22 июл 2007
    Сообщения:
    1.746
    Симпатии:
    0
    Адрес:
    Санкт-Петербург
    Elkaz, во-первых, картинка будет картинкой, а не php файлом. Во-вторых, тип файла берётся из расширения.

    +Sten+, и тип файла тоже возмётся из расширения.
     
  10. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    Elkaz
    Уважаемый, я не видел чтобы файлы с расширением jpg исполнялись как php файлы
     
  11. +Sten+

    +Sten+ Активный пользователь

    С нами с:
    27 авг 2007
    Сообщения:
    978
    Симпатии:
    0
    shreck
    Наример, на сервере стоит графический счётчик и админ связал jpg и PHP. Загруженная картинка вставится, например, как аватар и при вызове сделает что угодно.

    Так что вполне возможно серьезно похакать сайт, даже просто по забывчивости админа.

    Если загружать только картинки, стоит проверить, картинка ли это посредством GD функций, которые вернут фалос, если это не картинка вовсе.
     
  12. +Sten+

    +Sten+ Активный пользователь

    С нами с:
    27 авг 2007
    Сообщения:
    978
    Симпатии:
    0
    Я никогда не понимал, зачем делать приватными переменные, которые можно сделать публичными. В первом случае придётся писать дополнительный метод и вызывать его. Во втором случае я могу не только смотреть, есть ли ошибки, но и читать их.

    В этом всём есть какой-то тайный смысл?
     
  13. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    +Sten+
    Можно конечно настроить сервер, чтобы он исполнял файлы *.qwead как php. Но согласись, случай который ты привел, весьма редко встречается. Хотя все возможно.
     
  14. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    +Sten+
    Делай как считаешь нужным. В идеале, в любом классе все члены должны быть закрытыми, а изменение или чтение членов должно идти только через методы или свойства.
     
  15. +Sten+

    +Sten+ Активный пользователь

    С нами с:
    27 авг 2007
    Сообщения:
    978
    Симпатии:
    0
    shreck
    Ясно. Дело стиля.
    пс. Не хватает проверки "На пустоту". Если ничего не вводить, пишет всё загружено.
     
  16. shreck

    shreck Активный пользователь

    С нами с:
    7 авг 2007
    Сообщения:
    479
    Симпатии:
    0
    Адрес:
    Россия, Саратов
    пофиксено.

    зы: на сервер вечером залью, обновил первый пост в теме.
     
  17. vb

    vb Активный пользователь

    С нами с:
    6 июн 2006
    Сообщения:
    911
    Симпатии:
    0
    Адрес:
    Saint-Petersburg
    ГО, ламер, лень было почитать код да? :)))) (не рекомендуется повторять в домашних условиях обращение к ГО)
    Код (Text):
    1.  
    2. array(
    3.                                   UPLOAD_ERR_OK => 'Файл(ы) загружен(ы).',
    4.                                   UPLOAD_ERR_INI_SIZE => 'Превышен допустимый размер файла(ов) разрешенный настройками php.',
    Не объявленные константы, отсутствие мульти ланга, хотя мульти ланг это уже придирки пожалуй.
    Название констант отображает имя файла - не имя класса? Почему тогда имя класса не соответствует имени файла?
    Код (Text):
    1.  
    2. $encodeFileName
    3. $max_file_size
    Используется разный стиль написания переменных в рамках одного класса :).
    Код (Text):
    1.  
    2. # if(!$this->_isWritable()) {
    3. #          Throw new Exception('Директория не существует или защищена от записи');
    4. #        }
    5. //...
    6. # private function _isWritable() {
    7. #        return is_writable($this->path) ? true : false;
    8. #      }
    Напрямую к is_writable не обратиться - чтоб никто не догадался? :)
    Код (Text):
    1.  
    2. # public function getErrors() {
    3. #        foreach($this->errors as $e) {
    4. #          print '<font color="#ff0000">'.$e.'</font><br>';      
    5. #        }
    6. #      }
    Если уж это клас то фигль он выводы делает в браузер?
    Код (Text):
    1.  
    2. $this->fileinfo = $_FILES[$upldbox]
    Ну и обращение к суперглобальным переменным из класса - это как то не круто.

    дальше лень стало смотреть. :)
     
  18. Anonymous

    Anonymous Guest

    Вот именно это я и назвал "мелкими придирками" и не стал озвучивать.
     
  19. vb

    vb Активный пользователь

    С нами с:
    6 июн 2006
    Сообщения:
    911
    Симпатии:
    0
    Адрес:
    Saint-Petersburg
    Горбунов Олег
    а если класс на тривиальный функционал написан криво, зачем он нужен как класс да еще и в форуме "Решения" ? :)
     
  20. Anonymous

    Anonymous Guest

     
  21. vb

    vb Активный пользователь

    С нами с:
    6 июн 2006
    Сообщения:
    911
    Симпатии:
    0
    Адрес:
    Saint-Petersburg
  22. Anonymous

    Anonymous Guest

    Угу... зачем вообще программировать?
     
  23. sword dancer

    sword dancer Активный пользователь

    С нами с:
    17 фев 2008
    Сообщения:
    295
    Симпатии:
    0
    фигасе, мелкие придирки 0_0

    +Sten+, члены никому ничего не должны. свойства объекта - это такие же составляющие интерфейса, как и методы.
     
  24. sword dancer

    sword dancer Активный пользователь

    С нами с:
    17 фев 2008
    Сообщения:
    295
    Симпатии:
    0
    Горбунов Олег, чтобы делать то, что ещё никто не делал или делал, но не правильно. а вот какой смысл изобретать велосипед с треугольными колёсами?
     
  25. vb

    vb Активный пользователь

    С нами с:
    6 июн 2006
    Сообщения:
    911
    Симпатии:
    0
    Адрес:
    Saint-Petersburg
    А зачем? :)))
    шучу кончено. Ты ГОшка не путай, одно с другим. В данном случае мы рассматриваем "Готовое решение" которое представлено на суд общественности блестит всеми дырами и не является лучшим из имеющихся открытых решений :).
    А программить можно ради кайфухи, денеК и ради закрытых опенсорсов ;) бугага :).
    Данное решение не под один из критериев не подходит вот и все.

    А говоря о посредственном решении, что оно хорошее, вы все дружно тем самым сливаете человека. Он вместо того чтоб понять свои ошибки будет думать, что достиг каких то результатов. :-/
    Я против таких сливов :).