Comment gérer gracieusement les fichiers qui dépassent `post_max_size` de PHP?

86

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

J'ai appris qu'il y a deux paramètres php.iniqui affectent la taille maximale d'un téléchargement de fichier: upload_max_filesizeet 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 silencieusement et revient au formulaire vierge.

Existe-t-il un moyen d'attraper cette erreur?

Nathan Long
la source
1
Avez-vous accès à php.ini? post_max_size doit être défini plus grand que upload_max_filesize. Vous devez également utiliser <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> sur le formulaire comme indiqué ca2.php.net/manual/en/features.file-upload.post-method.php
Matt McCormick
@Matt McCormick - l'entrée MAX_FILE_SIZE fonctionne très bien - si la taille du fichier dépasse cela, la taille du fichier apparaît maintenant comme 0, ce qui est un cas que j'ai déjà traité. Même si cela peut être contourné par un utilisateur malveillant, cela sert mes objectifs ici, car j'essaie simplement d'échouer gracieusement pour les utilisateurs réguliers.
Nathan Long

Réponses:

55

De la documentation :

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

Donc, malheureusement, il ne semble pas que PHP envoie une erreur. Et comme il envoie un tableau $ _POST vide, c'est pourquoi votre script revient à la forme vierge - il ne pense pas 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 manière plus élégante est la comparaison entre post_max_size et $ _SERVER ['CONTENT_LENGTH']. Veuillez noter que ce dernier comprend non seulement la taille du fichier téléchargé plus les données de publication, mais également les séquences en plusieurs parties.

Matt McCormick
la source
3
Veuillez lire la partie en gras. C'est-à-dire que si la taille du fichier dépasse upload_max_filesize, l'utilisateur demande ce qui se passe lorsque le formulaire dépasse post_max_size. post_max_size doit être défini plus haut que upload_max_filesize pour essayer d'éviter ce problème, mais l'OP peut avoir des raisons de le conserver.
Matt McCormick
1
Désolé, j'ai confondu post_max_size et upload_max_filesize. +1
Pekka
@Matt - post_max_size EST défini plus haut que upload_max_filesize, mais j'obtiens toujours l'échec si le téléchargement dépasse les deux. S'il se situe entre les deux, je vois que la taille du fichier est de 0.
Nathan Long
1
Vous l'avez résolu, mais oui post_max_size est disponible. Faites simplement ini_get ('post_max_size'). ini_get () peut également être utilisé pour vérifier d'autres paramètres INI.
Matt McCormick
1
@SavasVedova - merci de l'avoir signalé, mais je ne peux plus résoudre le problème. Cela fait quelques années et je ne travaille plus dans cette entreprise ni en PHP. :)
Nathan Long
45

il existe un moyen d'attraper / de gérer les fichiers dépassant la taille maximale de l'article, c'est mon préféré, car il dit à 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...
}
AbdullahAJM
la source
2
Cela fonctionne lorsque track_errors = Off, éventuellement aussi display_errors = Off et display_startup_errors = Off dans php.ini. Sinon, PHP n'ira même pas aussi loin et enverra l'avertissement, comme dans le titre de la question. Mais dans un système de production, cela devrait être le paramètre php.ini, donc cela fonctionne très bien.
raoulsson
2
Cette idée géniale fonctionne bien dans mes tests (PHP / 5.5.8). Il peut également être amélioré en tenant $_SERVER['CONTENT_LENGTH']et upload_max_filesizeen compte.
Álvaro González
En partant du commentaire de raoulsson , existe-t-il un moyen de supprimer l'avertissement si vous n'êtes pas dans un environnement avec des erreurs supprimées?
brendo
6

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

Nous avons donc implémenté une vérification, 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;
}
staabm
la source
@ manuel-azar: les instructions "break" ajoutées ne sont pas correctes. ceux qui n'appartiennent pas là-bas .. voir 3v4l.org/ABfGs
staabm
Il n'est pas nécessaire de vérifier le chèque si la taille dépasse. Il n'y aurait pas de content_length s'il n'y avait pas de fichier. Il vous suffit donc de vérifier si content_length est défini et si les variables de publication et de fichier sont vides. Non?
ADJenks
4

En vous basant 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 le paramètre php_max_filesize:

            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>';
                }
            }
Lance Cleveland
la source
C'est la manière la plus logique de le faire. Vérifiez simplement si _post est vide mais que la longueur du contenu ne l'est pas. Pas besoin de comparer les tailles.
ADJenks
1

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;
}
Doglas
la source