ru en uk

  авторизация

(044) 362 48 16   (098) 294 41 60


   Цены

Основы закачки файлов


Согласно протоколу HTTP файлы можно закачивать двумя способами:

  • POST методом
  • PUT методом

Наиболее распространеный POST метод, PUT метод в настоящее время почти не используется. Чтобы броузер передал файл нужно поместить на страницу такой HTML код

<form enctype="multipart/form-data" method="post" action="upload_script.php"> 
<input type="hidden" name="MAX_FILE_SIZE" value="1000"> 
Выберите файл: <input name="имя_поля" type="file"> 
<input type="submit" value="Послать файл"> 
</form>


ОБЯЗАТЕЛЬНО нужно указывать <form enctype="multipart/form-data" method="post"...> и очень желательно <input type="hidden" name="MAX_FILE_SIZE" value="XXXX"> причем ДО поля ввода файла.

Когда эти условия выполнены, можно писать скрипт который будет обрабатывать закачку файлов.

<?php
function doUpload($field_name$overwrite=false$uniquename=false){
    if (!
is_array($field_name)) $field_name=Array($field_name);
#-------- FILE UPLOAD -----------------
// место хранения файлов
$storage="/home/www/public_html/uploads/";

// маленькие проверки на будущие глюки
$flag = (bool) ini_get("safe_mode");
if (
$flag||(!strstr($_SERVER['SERVER_SOFTWARE'], 'win'))){
 if (
getmyuid()!=fileowner($storage)){
  die(
"Safe mode uncompatibililty. Check owner for '".$storage"'");
 }
}
// разрешенные для закачки расширения (типы) файлов
$allowed=array(
 
'jpg',
 
'gif',
 
'png',
 
'pdf',
 
'doc',
 
'txt',
 
'rtf'
);

foreach (
$field_name as $field_i){

    
// максимальный размер файла. В любом случае он не может быть
    // больше чем upload_max_filesize=??M в php.ini (2Мб)
    // а также post_max_size=??M (8Мб)
    
$maxsize=61440// 60Kб

    // считываем имя закачиваемого файла
    
$filename=$_FILES[$field_i]['name'];

    
// считываем размер закачиваемого файла
    
$filename=$_FILES[$field_i]['size'];

    
// считываем расширение файла
    
$fileext=strtolower(substr(strrchr($filename,"."),1));

    
// запрещаем закачку неразрешенных типов, например PHP скриптов!!
    
if(!in_array($fileext$allowed)){
     die(
"Недопустимый тип файла");
    }

    
// запрещаем закачку слишком больших файлов
    
if($filesize>$maxsize){
     die(
"Слишком большой файл");
    }

    
// считываем имя файла, который закачан во временную папку
    // upload_tmp_dir= в файле php.ini
    
$tmpfname=$_FILES[$field_i]['tmp_name'])

    
// исправляем имя файла, удаляем недопустимые символы, пробелы.
    
$filename ereg_replace("[^a-z0-9._]""",
                
str_replace(" ""_",
                
str_replace("%20""_"strtolower($name))));

    if (
$filename=""){
     die(
"Недопустимое имя файла. Только английские буквы, цифры и '_'!");
    }

    
// полный путь к закачке файла
    
$filepath=$storage;
    if (
$uniquename){
     
$filepath=$filepath.time()."_";
    }
    
$filepath=$filepath.$filename;

     if (
is_uploaded_file($tmpfname) {

    
// если $overwrite!=true проверяем нету ли уже такого файла
      
if (!$overwrite){
       if (
file_exists($filepath)){
        die(
"Файл с именем <b>".$filename."</b> уже существует.
             Переименуйте файл или удалите его с сервера"
);
       }
      }

       
move_uploaded_file($tmpfname$filepath)
       or die(
"Ошибка закачки файла: ".$filename);
    
// Если пользователь Апача и FTP разные, например nobody и pupkin,
    // то чтобы иметь доступ по FTP (по умолчанию выставляется 0600)
    // поставьте 0644 или 0666 если хотите также перезаписывать по FTP
       
@chmod($filepath0644);
      }
     }
}
#------------- END FILE UPLOAD ----------
}

// вызвать функцию
doUpload('имя_поля');

// закачать несколько файлов сразу
doUpload(Array('имя_поля1','имя_поля2'));

// если нужно перезаписывать существующий файл
doUpload('имя_поля'true);

// если нужно сохранить существующий файл
// будет создано новое имя, н: pic.jpg -> 989181984_pic.jpg
doUpload('имя_поля'falsetrue);
?>


PHP версии 3 не имел функции move_uploaded_file() поэтому до выхода четвертой версии исполльзовалась функция copy() которая до сих пор находится в документации и пользуется популярностью. КРАЙНЕ НЕЖЕЛАТЕЛЬНО использовать copy() при закачке файлов, т.к. возможны многие глюки. copy() вообще не будет работать при включенном open_basedir!! В safe_mode=On чтобы использовать copy() нужно установить на временную папку того же ВЛАДЕЛЬЦА что и выполняет скрипт, тоесть аплоад будет невозможно делать если у вас несколько пользователей (как и должно быть в случае виртуальных доменов). Кроме того copy() не выполняет проверку файла на существование, не возвращает

Возможные глюки



  1. file_uploads=Off в php.ini. Закачка файлов запрещена
  2. Нету прав на $storage="/home/www/public_html/uploads/". Измените chown на пользователя под которым запущен скрипт или поставьте на папку chmod 0777
  3. Целевая директория имеет другого владельца чем под которым запущен PHP, когда safe_mode=On. Это повсеместно распространенный случай для шарового хостинга, когда пользователь Апача для примера www или nobody, тогда как доступ по FTP, например, для pupkin. Выхода два:

    • Долбить службу поддержки для настройки одинаковых юзеров на FTP и Apache (для вашего виртуального домена).
    • Папку для загрузки создать ИЗ скрипта под Апачем и поставить на неё 'chmod 0777'. Тогда Вы сможете работать из скриптов обходя safe_mode запреты и редактировать файлы по FTP.

  4. Нету прав на upload_tmp_dir=; (настройка в php.ini). Поставьте chmod 0777 на эту папку.
  5. Файлы могут быть испорченными если под Апачем запущены некоторые модули, например mod_charset (Также известный как Russian Apache). Выключите его для определенных файлов:

    <Files upload.php><br>
    CharsetDisable On<br>
    </Files>

  6. Не закачиваются большие файлы. Причин может быть несколько, если не выполняется хоть одно из условий, файл не закачается:

    • Размер файла больше $_POST['MAX_FILE_SIZE']
    • Размер файла больше upload_max_filesize=2M (php.ini)
    • Размер файла больше post_max_size=8M (php.ini)
    • Размер файла больше LimitRequestBody (httpd.conf)
    • Исчерпана дисковая квота или на upload_tmp_dir= или на целевую директорию
    • Время выполнения скрипта превысило max_execution_time= (php.ini)
    • Время выполнения скрипта превысило Timeout 30 (httpd.conf)
    • Время выполнения скрипта превысило таймаут для CGI (Консоль IIS)

  7. Пользователь сидит за прокси который запрещает передачу
  8. Вы использовали ДРУГОЙ способ закачки, чем в этой статье, например сокращенный синтаксис, при том что register_globals=Off или старая версия PHP или, ещё хуже - использовали copy() вместо move_uploaded_file().
  9. Неправильно работает $HTTP_POST_FILES[$field_name]['type']. Это не глюк PHP, этот параметр передаётся броузером, так что НИКОГДА не полагайтесь на него.
  10. Проблемы с НЕброузерной закачкой файла (Не URI encoded форма). В большинстве случаев для этого понадобится использовать $HTTP_RAW_POST_DATA
  11. Проблемы с закачкой файлов нулевой длины. Суть проблемы не в том что файлы не закачиваются, а в том что невозможно определить закачался ли файл на самом деле. Многие люди проверяют статус закачки через $HTTP_POST_FILES[$field_name]['size'] но как в случае когда файл не закачался, так и в случае пустого файла, переменная будет равна 0. Если пользователь сам напечатает имя несуществующего файла в поле броузера, он передастся как файл нулевой длины. Проверить это средствами PHP нельзя.
  12. magic_quotes_gpc=On и stripslashes на Win платформе создаст проблемы с получением имен файлов, так например $HTTP_POST_FILES[$field_name]['size'] всё-таки должен содеражть двойные бек-слеши.
  13. Неправильные параметры переданы в move_uploaded_file(). Там должно быть что-то вроде:

    <?php
    // правильный синтаксис
    move_uploaded_file(
    '/var/php_tmp_upload/userfile.jpg',
    '/home/pupkin/public_html/userfile.jpg'
    );

    // неправильный синтаксис
    move_uploaded_file(
    'userfile.jpg',
    '/home/pupkin/public_html/userfile.jpg'
    );
    ?>




 
Блокировка файлов
29.05.2007
"Warning! On most operation systems flock() is implemented at the process level. When using a multithreaded server API like ISAPI you cannot rely on flock() to protect files against other PHP scripts running in parallel threads of the same server instance!"
Основы безопасности
29.05.2007
Сразу скажем что PHP и Апач в этой области далеко не продвинулись. Нормальная многопользовательская конфигурация веб-сервера должна работать под разными пользователями...
Введение в PHP5
29.05.2007
PHP5 ещё официально не вышел, но "рабочие" версии уже трудоспособны (равно как и нестабильны!), так что мы вполне можем начать изучение новых возможностей грядущего релиза PHP и попрактиковать с ними. В этой статье мы поговорим о трёх основных нововведениях в PHP5

 

Rambler's Top100