Настройка параметров PHP

Существует несколько настроек конфигурации PHP, которые вам следует предварительно проверить для успешной загрузки файлов. В этом разделе мы рассмотрим все параметры, которые важны для загрузки файлов PHP. Эти параметры можно настроить в файле php.ini.

Если вы не знаете, где найти файл php.ini, вы можете использовать php_ini_loaded_file(), чтобы найти его. Просто создайте файл PHP на своем сервере со следующей строкой и откройте его из браузера:

echo php_ini_loaded_file();

Вот выдержка из установочного файла с некоторыми полезными значениями по умолчанию:

; Whether to allow HTTP file uploads.
file_uploads = On
 
; Temporary directory for HTTP uploaded files.
; Will use system default if not set.
;upload_tmp_dir = 
 
; Maximum allowed size for uploaded files.
upload_max_filesize = 16M
 
; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20
 
; Maximum size of POST data that PHP will accept.
post_max_size = 20M
 
max_input_time = 60
memory_limit = 128M
max_execution_time = 30

- Ключевые настройки

file_uploads

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

upload_tmp_dir

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

upload_max_filesize

Директива upload_max_filesize позволяет вам настроить максимальный размер загруженного файла. По умолчанию он установлен в 2M (два мегабайта), и вы можете переопределить этот параметр. Два мегабайта не очень соответствуют сегодняшним стандартам, поэтому вам, возможно, придется это увеличить. Если вы получите сообщение об ошибке - file exceeds upload_max_filesize при попытке загрузить файл, вам необходимо увеличить это значение. Если вы это сделаете, обязательно увеличьте post_max_size.

max_file_uploads

Это позволяет вам установить максимальное количество файлов, которые могут быть загружены за раз. По умолчанию 20, разумное количество.

post_max_size

Директива post_max_size позволяет настроить максимальный размер данных POST. Поскольку файлы загружаются с помощью POST-запросов, это значение должно быть больше, чем указано в директиве upload_max_filesize. Например, если ваш upload_max_filesize составляет 16M (16 мегабайт), вам может потребоваться установить post_max_size в 20M.

max_input_time

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

memory_limit

Директива memory_limit указывает максимальный объем памяти, который может потреблять скрипт. Если вы сталкиваетесь с проблемами при загрузке больших файлов, вам необходимо убедиться, что значение этой директивы больше, чем указано в директиве post_max_size. Значение по умолчанию - 128M (128 мегабайт), поэтому, если у вас нет очень больших post_max_size и upload_max_filesize, вам не нужно беспокоиться об этом.

max_execution_time

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


Создание формы HTML

После того, как вы настроили параметры PHP, вы готовы испытать возможности загрузки файлов PHP.

Мы собираемся создать два файла PHP: index.php и upload.php. Файл index.php содержит код, который отвечает за отображение формы загрузки файла. С другой стороны, файл upload.php отвечает за загрузку файла на сервер.

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

Давайте посмотрим на файл index.php:

<?php
    session_start(); 
?>
<!DOCTYPE html>
<html>
    <head>
        <title>PHP File Upload</title>
    </head>
    <body>
        <?php
            if (isset($_SESSION['message']) && $_SESSION['message']) {
                printf('<b>%s</b>', $_SESSION['message']);
                unset($_SESSION['message']);
            }
        ?>
        <form method="POST" action="upload.php" enctype="multipart/form-data">
            <div>
                <span>Upload a File:</span>
                <input type="file" name="uploadedFile" />
            </div>
            
            <input type="submit" name="uploadBtn" value="Upload" />
        </form>
    </body>
</html>

Хотя это может показаться типичной формой PHP, существует важное различие в значении атрибута enctype тега <form>. Он должен быть установлен в multipart/form-data, так как форма содержит поле файла.

Атрибут enctype указывает тип кодировки, который должен использоваться при отправке формы, и он принимает одно из следующих трех значений:

  • application/x-www-form-urlencoded: Это значение по умолчанию, когда вы не устанавливаете значение атрибута enctype явно. В этом случае символы кодируются перед отправкой на сервер. Если у вас нет поля файла в вашей форме, вы должны использовать это значение для атрибута enctype.
  • multipart/form-data: Когда вы используете значение multipart/form-data для атрибута enctype, оно позволяет загружать файлы с использованием метода POST. Кроме того, он гарантирует, что символы не кодируются при отправке формы.
  • text/plain: Обычно это не используется. С помощью этой настройки данные отправляются без кодирования.

Затем мы выводим поле файла, которое позволяет вам выбрать файл с вашего компьютера.

<input type="file" name="uploadedFile" />

Кроме того, мы отобразили сообщение в верхней части формы. Это сообщение показывает статус загрузки файла, и оно будет установлено в переменной сессии скриптом upload.php.


Логика загрузки

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

upload.php:

session_start();

$message = ''; 

if (isset($_POST['uploadBtn']) && $_POST['uploadBtn'] == 'Upload') {
    if (isset($_FILES['uploadedFile']) && $_FILES['uploadedFile']['error'] === UPLOAD_ERR_OK) {
        // get details of the uploaded file
        $fileTmpPath = $_FILES['uploadedFile']['tmp_name'];
        $fileName = $_FILES['uploadedFile']['name'];
        $fileSize = $_FILES['uploadedFile']['size'];
        $fileType = $_FILES['uploadedFile']['type'];
        $fileNameCmps = explode(".", $fileName);
        $fileExtension = strtolower(end($fileNameCmps));
        
        // sanitize file-name
        $newFileName = md5(time() . $fileName) . '.' . $fileExtension;

        // check if file has one of the following extensions
        $allowedfileExtensions = array('jpg', 'gif', 'png', 'zip', 'txt', 'xls', 'doc');

        if (in_array($fileExtension, $allowedfileExtensions)) {
            // directory in which the uploaded file will be moved
            $uploadFileDir = './uploaded_files/';
            $dest_path = $uploadFileDir . $newFileName;

            if(move_uploaded_file($fileTmpPath, $dest_path)) {
                $message = 'File is successfully uploaded.';
            } else {
                $message = 'There was some error moving the file to upload directory. Please make sure the upload directory is writable by web server.';
            }
        } else {
            $message = 'Upload failed. Allowed file types: ' . implode(',', $allowedfileExtensions);
        }
    } else {
        $message = 'There is some error in the file upload. Please check the following error.<br>';
        $message .= 'Error:' . $_FILES['uploadedFile']['error'];
    }
}

$_SESSION['message'] = $message;

header("Location: index.php");

Мы рассмотрим важные части этого файла. В файле upload.php мы проверили, действительно ли это валидный запрос POST.

В PHP, когда файл загружается, суперглобальная переменная $_FILES заполняется всей информацией о загруженном файле. Она инициализируется как массив и может содержать следующую информацию для успешной загрузки файла.

  • tmp_name: Временный путь, в который загружается файл, сохраняется в этой переменной.
  • name: Фактическое имя файла сохраняется в этой переменной.
  • size: Указывает размер загруженного файла в байтах.
  • type: Содержит mime тип загруженного файла.
  • error: Если во время загрузки файла была ошибка, эта переменная заполняется соответствующим сообщением об ошибке. В случае успешной загрузки файла она содержит значение 0, которое можно сравнить с помощью константы UPLOAD_ERR_OK.

После проверки запроса POST мы проверяем, что загрузка файла прошла успешно. Вы можете видеть, что переменная $_FILES является многомерным массивом, первый элемент - это имя поля файла, а второй элемент содержит информацию о загруженном файле, как мы уже говорили выше.

Если загрузка файла прошла успешно, мы инициализируем несколько переменных с информацией о загруженном файле.

В приведенном выше коде мы также выяснили расширение загруженного файла и сохранили его в переменной $fileExtension. Поскольку загруженный файл может содержать пробелы и другие специальные символы, лучше очистить имя файла.

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

Наконец, мы используем функцию move_uploaded_file для перемещения загруженного файла в определенное место по нашему выбору. Функция move_uploaded_file принимает два аргумента. Первым аргументом является имя загруженного файла, а второй аргумент - путь назначения, в который вы хотите переместить файл.

Наконец, мы перенаправляем пользователя в файл index.php. Кроме того, мы устанавливаем соответствующее сообщение в переменной сессии, которое будет отображаться пользователям после перенаправления в файле index.php.


Загрузка нескольких файлов

index.php:

<?php
    session_start();
?>
<!DOCTYPE html>
<html>
    <head>
        <title>PHP File Upload</title>
    </head>
    <body>
        <?php
            if (isset($_SESSION['message']) && $_SESSION['message']) {
                printf('<b>%s</b>', $_SESSION['message']);
                unset($_SESSION['message']);
            }
        ?>
        <form method="POST" action="upload.php" enctype="multipart/form-data">
            <div>
                <span>Upload a File:</span>
                <input type="file" name="uploadedFile[]" multiple />
            </div>
            
            <input type="submit" name="uploadBtn" value="Upload" />
        </form>
    </body>
</html>

upload.php:

session_start();

$message = ''; 

if (isset($_POST['uploadBtn']) && $_POST['uploadBtn'] == 'Upload') {
    // Create new files array
    foreach($_FILES['uploadedFile'] as $key => $value) {
        foreach($value as $k => $v) {
            $_FILES['uploadedFile'][$k][$key] = $v;
        }
        
        // Delete old keys
        unset($_FILES['uploadedFile'][$key]);
    }
    
    foreach ($_FILES['uploadedFile'] as $k => $v) {
        // get details of the uploaded file
        $fileTmpPath = $_FILES['uploadedFile'][$k]['tmp_name'];
        $fileName = $_FILES['uploadedFile'][$k]['name'];
        $fileSize = $_FILES['uploadedFile'][$k]['size'];
        $fileType = $_FILES['uploadedFile'][$k]['type'];
        $fileErrorCode = $_FILES['uploadedFile'][$k]['error'];
        $fileNameCmps = explode(".", $fileName);
        $fileExtension = strtolower(end($fileNameCmps));
        
        if ($fileErrorCode === UPLOAD_ERR_OK) {
            $newFileName = md5(time() . $fileName) . '.' . $fileExtension;
            
            // check if file has one of the following extensions
            $allowedfileExtensions = array('jpg', 'gif', 'png', 'zip', 'txt', 'xls', 'doc');

            if (in_array($fileExtension, $allowedfileExtensions)) {
                // directory in which the uploaded file will be moved
                $uploadFileDir = './uploaded_files/';
                $dest_path = $uploadFileDir . $newFileName;

                if(move_uploaded_file($fileTmpPath, $dest_path)) {
                    $message .= 'File ' . $fileName . ' is successfully uploaded. <br>';
                } else {
                    $message .= 'There was some error moving the file ' . $fileName . ' to upload directory. Please make sure the upload directory is writable by web server. <br>';
                }
            } else {
                $message .= 'Upload failed for file ' . $fileName . '. Allowed file types: ' . implode(',', $allowedfileExtensions) . '<br>';
            }
        } else {
            $message .= 'There is some error in the file ' . $fileName . ' upload. Please check the following error.<br>';
            $message .= 'Error: ' . $fileErrorCode['error'];
        }
    }
}

$_SESSION['message'] = $message;

header("Location: index.php");

Объяснение сообщений об ошибках

PHP возвращает код ошибки наряду с другими атрибутами принятого файла. Он расположен в массиве, создаваемом PHP при загрузке файла, и может быть получен при обращении по ключу error. Другими словами, код ошибки можно найти в $_FILES['uploadedFile']['error'].

  • UPLOAD_ERR_OK - Значение: 0; Ошибок не возникло, файл был успешно загружен на сервер.
  • UPLOAD_ERR_INI_SIZE - Значение: 1; Размер принятого файла превысил максимально допустимый размер, который задан директивой upload_max_filesize конфигурационного файла php.ini.
  • UPLOAD_ERR_FORM_SIZE - Значение: 2; Размер загружаемого файла превысил значение MAX_FILE_SIZE, указанное в HTML-форме.
  • UPLOAD_ERR_PARTIAL - Значение: 3; Загружаемый файл был получен только частично.
  • UPLOAD_ERR_NO_FILE - Значение: 4; Файл не был загружен.
  • UPLOAD_ERR_NO_TMP_DIR - Значение: 6; Отсутствует временная папка.
  • UPLOAD_ERR_CANT_WRITE - Значение: 7; Не удалось записать файл на диск.
  • UPLOAD_ERR_EXTENSION - Значение: 8; PHP-расширение остановило загрузку файла. PHP не предоставляет способа определить, какое расширение остановило загрузку файла; в этом может помочь просмотр списка загруженных расширений с помощью phpinfo().