web-dev-qa-db-fra.com

Comment gérer avec élégance les fichiers qui dépassent le `post_max_size` de PHP?

Je travaille sur un formulaire PHP qui joint un fichier à un e-mail, et j'essaie de gérer avec élégance les cas où le fichier téléchargé est trop volumineux.

J'ai appris qu'il existe deux paramètres dans php.ini qui affectent la taille maximale d'un téléchargement de fichier: upload_max_filesize et post_max_size.

Si la taille d'un fichier dépasse upload_max-filesize, PHP renvoie la taille du fichier à 0. C'est très bien, je peux vérifier cela.

Mais s'il dépasse post_max_size, mon script échoue en silence et revient au formulaire vierge.

Existe-t-il un moyen de détecter cette erreur?

82
Nathan Long

De la documentation :

Si la taille des données de publication est supérieure à post_max_size, les $ _ POST et $ _FILES superglobals sont vides . Cela peut être suivi de différentes manières, par ex. en transmettant la variable $ _GET au script traitant les données, c'est-à-dire <form action = "edit.php? traitées = 1">, puis en vérifiant si $ _GET ['traitées'] est défini.

Donc, malheureusement, il ne ressemble pas à PHP envoie une erreur. Et comme il envoie un tableau $ _POST vide, c'est pourquoi votre script revient au formulaire vierge - il ne le fait pas pense que c'est un POST. (Assez mauvaise décision de conception à mon humble avis)

Ce commentateur a également une idée intéressante.

Il semble qu'une méthode plus élégante soit la comparaison entre post_max_size et $ _SERVER ['CONTENT_LENGTH']. Veuillez noter que ce dernier inclut non seulement la taille du fichier téléchargé plus les données de publication, mais également les séquences en plusieurs parties.

52
Matt McCormick

il existe un moyen d'attraper/gérer les fichiers dépassant la taille maximale du message, c'est mon préféré, car il indique à l'utilisateur final ce qui s'est passé et qui est en faute;)

if (empty($_FILES) && empty($_POST) &&
        isset($_SERVER['REQUEST_METHOD']) &&
        strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
    //catch file overload error...
    $postMax = ini_get('post_max_size'); //grab the size limits...
    echo "<p style=\"color: #F00;\">\nPlease note files larger than {$postMax} will result in this error!<br>Please be advised this is not a limitation in the CMS, This is a limitation of the hosting server.<br>For various reasons they limit the max size of uploaded files, if you have access to the php ini file you can fix this by changing the post_max_size setting.<br> If you can't then please ask your Host to increase the size limits, or use the FTP uploaded form</p>"; // echo out error and solutions...
    addForm(); //bounce back to the just filled out form.
}
else {
    // continue on with processing of the page...
}
40
AbdullahAJM

Nous avons eu le problème pour SOAP requêtes où une vérification de la vacuité de $ _POST et $ _FILES ne fonctionne pas, car elles sont également vides sur les demandes valides.

Par conséquent, nous avons implémenté une vérification, en comparant CONTENT_LENGTH et post_max_size. L'exception levée est ensuite transformée en XML-SOAP-FAULT par notre gestionnaire d'exceptions enregistré.

private function checkPostSizeExceeded() {
    $maxPostSize = $this->iniGetBytes('post_max_size');

    if ($_SERVER['CONTENT_LENGTH'] > $maxPostSize) {
        throw new Exception(
            sprintf('Max post size exceeded! Got %s bytes, but limit is %s bytes.',
                $_SERVER['CONTENT_LENGTH'],
                $maxPostSize
            )
        );
    }
}

private function iniGetBytes($val)
{
    $val = trim(ini_get($val));
    if ($val != '') {
        $last = strtolower(
            $val{strlen($val) - 1}
        );
    } else {
        $last = '';
    }
    switch ($last) {
        // The 'G' modifier is available since PHP 5.1.0
        case 'g':
            $val *= 1024;
            // fall through
        case 'm':
            $val *= 1024;
            // fall through
        case 'k':
            $val *= 1024;
            // fall through
    }

    return $val;
}
6
staabm

En s'appuyant sur les réponses de @Matt McCormick et @ AbdullahAJM, voici un cas de test PHP qui vérifie que les variables utilisées dans le test sont définies, puis vérifie si $ _SERVER ['CONTENT_LENGTH'] dépasse la valeur php_max_filesize réglage:

            if (
                isset( $_SERVER['REQUEST_METHOD'] )      &&
                ($_SERVER['REQUEST_METHOD'] === 'POST' ) &&
                isset( $_SERVER['CONTENT_LENGTH'] )      &&
                ( empty( $_POST ) )
            ) {
                $max_post_size = ini_get('post_max_size');
                $content_length = $_SERVER['CONTENT_LENGTH'] / 1024 / 1024;
                if ($content_length > $max_post_size ) {
                    print "<div class='updated fade'>" .
                        sprintf(
                            __('It appears you tried to upload %d MiB of data but the PHP post_max_size is %d MiB.', 'csa-slplus'),
                            $content_length,
                            $max_post_size
                        ) .
                        '<br/>' .
                        __( 'Try increasing the post_max_size setting in your php.ini file.' , 'csa-slplus' ) .
                        '</div>';
                }
            }
4
Lance Cleveland

C'est un moyen simple de résoudre ce problème:

Appelez simplement "checkPostSizeExceeded" au début de votre code

function checkPostSizeExceeded() {
        if (isset($_SERVER['REQUEST_METHOD']) and $_SERVER['REQUEST_METHOD'] == 'POST' and
            isset($_SERVER['CONTENT_LENGTH']) and empty($_POST)//if is a post request and $_POST variable is empty(a symptom of "post max size error")
        ) {
            $max = get_ini_bytes('post_max_size');//get the limit of post size 
            $send = $_SERVER['CONTENT_LENGTH'];//get the sent post size

            if($max < $_SERVER['CONTENT_LENGTH'])//compare
                throw new Exception(
                    'Max size exceeded! Were sent ' . 
                        number_format($send/(1024*1024), 2) . 'MB, but ' . number_format($max/(1024*1024), 2) . 'MB is the application limit.'
                    );
        }
    }

N'oubliez pas de copier cette fonction auxiliaire:

function get_ini_bytes($attr){
    $attr_value = trim(ini_get($attr));

    if ($attr_value != '') {
        $type_byte = strtolower(
            $attr_value{strlen($attr_value) - 1}
        );
    } else
        return $attr_value;

    switch ($type_byte) {
        case 'g': $attr_value *= 1024*1024*1024; break;
        case 'm': $attr_value *= 1024*1024; break;
        case 'k': $attr_value *= 1024; break;
    }

    return $attr_value;
}
1
Doglas