Блог


Загрузка файлов

Часто возникает ситуация, когда нужно обеспечить пользователю возможность самостоятельно загрузить на сервер какой то файл. К примеру аватарку на форум.
Так вот - загрузка файлов не очень сложная операция на самом деле, если усвоить несколько моментов. Начнем по порядку.

Нам понадобится форма загрузчика:

1
2
3
4
5
6
<form action="file_upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="Закачать" name="upload" />
</form>


Главное, чем отличается форма загрузки файлов от обычной формы, это атрибут ecntype="multipart/form-data", который как раз и указывает на то, что будет загружаться файл. Для загрузки файлов надо использовать метод POST. 
 Тег <input> с атрибутом file позволяет пользователю выбрать загружаемый файл. 

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

Как текстовые данные можно получить через суперглобальный массив $_POST, так и загруженные файлы через $_FILES['имя']. Где имя - значение атрибута name файлового поля. В нашем случае это name="file".

$_FILES['file'] представляет собой массив и содержит следующие элементы:

$_FILES['file']['name'] - имя файла указанное при загрузке
$_FILES['file']['type'] - тип mime (напр.: image/jpeg)
$_FILES['file']['tmp_name'] - путь к временному файлу.
$_FILES['file']['size'] - размер файла в байтах
$_FILES['file']['error'] - код ошибки.

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

1
 2
 3
 4
 5
 6
<?php

    
echo '<pre>'
    
print_r($_FILES['file']); 
    echo 
'</pre>';

Результатом должно быть примерно это:

1
 2
 3
 4
 5
 6
 7
 8
Array 

    [name] => 1.jpg 
    [type] => image/jpeg 
    [tmp_name] => D:\xampp\tmp\php463.tmp 
    [error] => 0 
    [size] => 3415 
)

Ну это только посмотреть. А вот чтобы был толк, нужно сделать этот самый обработчик. Допустим мы хотим загрузить картинку (1.jpg)
Сначала валидация (проверка загружаемого файла). Проверим, что файл был успешно загружен во временную папку. Это такая директория, куда складываются все поступившие файлы до их проверки и дальнейшего перемещения. Своего рода карантин. После отработки скрипта, эти файлы уничтожаются.

Если ошибки загрузки не было, то $_FILES['file']['error'] возвращает 0, что соответствует именованной константе UPLOAD_ERR_OK (Вот список всех возможных ошибок и их значений). Если же случилась какая-то бяка, то вычисляем ее и выводим соответствующее сообщение.

А вот и он, собственной персоной. Прямо в том же файле
file_upload.php

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
<?php


$errors 
= array(); 
$info   ''

if(
$_FILES['file']['error'] === UPLOAD_ERR_OK

    
// если файл загружен без ошибок, то продолжим тут 
}  
else  

    
// массив ошибок 
    
$error_values = array(
 
    
UPLOAD_ERR_INI_SIZE   => 'Размер файла больше разрешенного 
                              директивой upload_max_filesize в php.ini'

    
UPLOAD_ERR_FORM_SIZE  => 'Размер файла превышает указанное 
                              значение в MAX_FILE_SIZE'

    
UPLOAD_ERR_PARTIAL    => 'Файл был загружен только частично'
    
UPLOAD_ERR_NO_FILE    => 'Не был выбран файл для загрузки'
    
UPLOAD_ERR_NO_TMP_DIR => 'Не найдена папка для временных файлов'
    
UPLOAD_ERR_CANT_WRITE => 'Ошибка записи файла на диск' 
    
); 

     
            
    if(!empty(
$error_values[$_FILES['file']['error']])) 
       
$errors[] = $error_values[$_FILES['file']['error']]; 
    else 
       
$errors[] = 'Случилось что-то непонятное'

}

В массив $errors будем записывать ошибки. А в $info сообщение, если загрузка прошла успешно.

Затем "более тонкая настройка". Проверка уже загруженного во временную директорию файла и перемещение его на постоянное место жительства.

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27

// если файл загружен без ошибок, то продолжаем тут 

// проверяем расширение файла 
    
$file_ext  pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION); 
    
$valid_ext = array('jpg''jpeg'); 

    if(
in_array($file_ext, $valid_ext)) 
    {
// проверяем размер файла 
        
$max_size 100000

        if(
$_FILES['file']['size'] < $max_size
        { 
// если все ОК 
           
$dest 'images/'. $_FILES['file']['name'];
 
           if(
move_uploaded_file($_FILES['file']['tmp_name'], $dest)) 
              
$info 'Файл успешно загружен'
           else 
              
$error 'Не удалось загрузить файл'
        }  
        else 
            
$error 'Размер файла больше допустимого'
    }  
    else 
        
$error 'У файла недопустимое расширение';

Проверим расширение. Сделаем это с помощью функции pathinfo(). Эта функции принимает в качестве параметра имя файла или путь к файлу и возвращает массив со следующими значениями:
dirname - путь к папке с файлом
basename - полное имя файла
extension - расширение файла
filename - имя без расширения ( с версии 5.2.0 )
Если нам нужно только одно из этих значений, то в опциях можем указать нужный флаг.
Например так - pathinfo($path, PATHINFO_EXTENSION) возвращает только расширение файла.
Затем проверяем его с помощью функции in_array().

Далее проверяем размер файла.

Если все прошло без ошибок, то перемещаем файл из временной папки в нужную нам. Для этого используем
move_uploaded_file($filename, $dest), где $filename - путь к файлу, который надо переместить, а $dest - путь, куда мы хотим сохранить наш файл вместе с именем.

При этом проверяем, что файл был действительно перемещен. Этому могут помещать, например, неправильно настроенные права на соответствующие папки. В Windows таких проблем практически не возникает, а вот в ОС *nix сплошь и рядом. Для нормальной работы папке, куда будут перемещаться файлы, нужно назначить права 777.

И в завершении выводим сообщение об ошибке или об успешной загрузке файла.

1
 2
 3
 4
 5

// выводим сообщение о результате 
    
echo !empty($error) ? $error ''
    echo !empty(
$info) ? $info '';

Но будет лучше и удобнее, если это оформить функцией.
file_upload.php

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
<?php


/**  
 * Function of uploaded of a file   
 * Функция загрузки файла (аплоадер)  
 * @param  int    $max_size    максимальный размер файла в килобайтах  
 * @param  array  $valid_ext   массив допустимых расширений  
 * @param  string $up_dir      директория загрузки  
 * @return array               сообщение о ходе выполнения  
 *  
 * @author IT studio IRBIS-team (www.irbis-team.com)  
 * @copyright © 2014 IRBIS-team   
 */  
    
function uploadHandle($max_size 100$valid_ext = array(), $up_dir '.')  
    {  
      
        
$error null;  
        
$info  null;  
        
$max_size *= 1024;  

        if(
$_FILES['file']['error'] === UPLOAD_ERR_OK)  
        {
// проверяем расширение файла  
            
$file_ext pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
  
            if(
in_array($file_ext$valid_ext))  
            {  
// проверяем размер файла  
                
if($_FILES['file']['size'] < $max_size)  
                {   // формируем путь для заагрузки
                    $dest $up_dir .'/' $_FILES['file']['name'];  
                    // перемещаем файл 
                    if(
move_uploaded_file($_FILES['file']['tmp_name'], $dest))  
                        
$info 'Файл успешно загружен';  
                    else  
                        
$error 'Не удалось загрузить файл';  
                }   
                else  
                    
$error 'Размер файла больше допустимого';  
            }   
            else  
                
$error 'У файла недопустимое расширение';  
        }   
        else  
        {  
            
// массив ошибок  
            
$error_values = array(
 
            
UPLOAD_ERR_INI_SIZE   => 'Размер файла больше разрешенного 
                                      директивой upload_max_filesize в php.ini'

            
UPLOAD_ERR_FORM_SIZE  => 'Размер файла превышает указанное 
                                      значение в MAX_FILE_SIZE'

            
UPLOAD_ERR_PARTIAL    => 'Файл был загружен только частично'
            
UPLOAD_ERR_NO_FILE    => 'Не был выбран файл для загрузки'
            
UPLOAD_ERR_NO_TMP_DIR => 'Не найдена папка для временных файлов'
            
UPLOAD_ERR_CANT_WRITE => 'Ошибка записи файла на диск' 
            
); 
      
            
$error_code $_FILES['userfile']['error'];  
      
            if(!empty(
$error_values[$error_code]))  
                
$error $error_values[$error_code];  
            else  
                
$error 'Случилось что-то непонятное';  
        }  
      
        return array(
'info' => $info'error' => $error);  
    }  
      
/////////////////////////////////////////////////////////////////////      
    
$extensions = array('jpg''jpeg''png''gif');  
    
$upload_dir 'images';  
      
    
// Запускаем функцию  
    
if(!empty($_POST['upload'])) 
    {      
        
$message uploadHandle(200$extensions$upload_dir);  
    
        
// Выводим сообщение  
        
echo $message['error'] ? $message['error'] : $message['info'];  
    } 
?>  

<form action="file_upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="Закачать" name="upload" />
</form>

Это самый простой загрузчик, он позволяет загружать любые файлы, не только картинки. Допустим архивы. Стоит только указать нужное расширение.
Попозже мы рассмотрим, как работать с картинками(ресайз, водный знак и так далее).

 

Бакаров Тимур aka infest

Теги: PHP | HTML

Комментарии (7)

killervampire
19-02-2014
Да уже на дворе 21 век - пора писать статьи про AJAX загрузку файлов - а это уже не актуально - но для новичков сойдёт!
Сергей
21-02-2014
Собственно обработчик то  что при ajax, что при такой загрузке, не отличаются. И там и тут проверки одни и теже.
killervampire
21-02-2014
Так вот какраз и хочется посмотреть как они этот обработчик при асинхронном запросе выполнят... там ведь есть где развернутся...
Медведь
30-05-2014
// проверяем расширение файла

Читал в сети, что его можно подделать. И предлагаются разные способы.

1

1
2
3

if($_FILES['file']['type']!="image/jpeg")



2

1
2
3
4

$imageinfo=getimagesize($_FILES['file']['tmp_name']);
if($imageinfo['mime']!="image/jpeg"



И Ваш вариант:

1
2
3
4
5

            $file_ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
  
            if(in_array($file_ext, $valid_ext)) 



Что тогда использовать?
twin
30-05-2014
Подделать можно, да. Первые два варианта. Если подделывать расширение, то в чем смысл... Ведь запустить исполняемы код с расширением .jpeg невозможно.
Олег
04-02-2015
Спасибо за статейку, мне помогла)
Николай
05-10-2015
Орфография: При этом проверяем, что файл был действительно перемещен. Этому могут помещать, например, неправильно настроенные права на соответствующие папки.

Хорошая статья, спасибо! Очень понятным языком написана. Подскажите, хотя бы на словах, как сделать путь к папке "динамичным". Допустим есть папки с именами 01, 02, 03, ... 10, 11, 12. И если месяц на дворе октябрь - то, чтобы загружалось в папку "10"?

 
Наверх