94 votes

Comment gérer élégamment les fichiers qui dépassent la `post_max_size` de PHP ?

Je travaille sur un formulaire PHP qui joint un fichier à un e-mail, et j'essaie de gérer de manière élégante 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 retourne la taille du fichier comme étant 0. C'est bien ; je peux vérifier cela.

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

Y a-t-il un moyen de capturer cette erreur ?

1 votes

Avez-vous accès au php.ini? post_max_size doit être réglé plus grand que upload_max_filesize. Vous devriez également utiliser sur le formulaire comme indiqué ca2.php.net/manual/en/features.file-upload.post-method.php

0 votes

@Matt McCormick - l'entrée MAX_FILE_SIZE fonctionne très bien - si la taille du fichier dépasse cela, la taille du fichier s'affiche maintenant comme 0, ce qui est un cas que j'ai déjà pris en charge. Même si cela peut être contourné par un utilisateur malveillant, cela répond à mes besoins ici, car je cherche simplement à échouer de manière élégante pour les utilisateurs réguliers.

58voto

Matt McCormick Points 3119

De la documentation :

Si la taille des données post dépasse post_max_size, les superglobales $_POST et $_FILES sont vides. Cela peut être suivi de différentes façons, par exemple en passant la variable $_GET au script traitant les données, c'est-à-dire

, puis en vérifiant si $_GET['processed'] est défini.

Malheureusement, il semble que PHP ne renvoie pas d'erreur. Et étant donné qu'il envoie un tableau $_POST vide, c'est pourquoi votre script revient au formulaire vide - il ne pense pas qu'il s'agisse d'un POST. (Une décision de conception assez pauvre à mon avis)

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

Il semble qu'une façon plus élégante est 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 post, mais aussi les séquences multipart.

4 votes

Veuillez lire la partie en gras. C'est si la taille du fichier dépasse upload_max_filesize, l'utilisateur demande ce qu'il se passe lorsque le formulaire dépasse post_max_size. post_max_size doit être réglé plus haut que upload_max_filesize pour essayer d'éviter ce problème, mais l'OP peut avoir des raisons de le garder le même.

1 votes

Désolé, j'ai confondu post_max_size et upload_max_filesize. +1

0 votes

@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 affiche 0.

47voto

AbdullahAJM Points 151

Il y a un moyen de capturer / gérer les fichiers dépassant la taille maximale de l'envoi, c'est ma préférée, car elle 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') {
    // attraper l'erreur de surcharge de fichiers...
    $postMax = ini_get('post_max_size'); // récupérer les limites de taille...
    echo "\nVeuillez noter que les fichiers supérieurs à {$postMax} entraîneront cette erreur !Veuillez noter qu'il ne s'agit pas d'une limitation du CMS, mais d'une limitation du serveur d'hébergement.Pour diverses raisons, ils limitent la taille maximale des fichiers téléchargés. Si vous avez accès au fichier php.ini, vous pouvez résoudre ce problème en modifiant le paramètre post_max_size. Si vous ne pouvez pas le faire, veuillez demander à votre hébergeur d'augmenter les limites de taille, ou utilisez le formulaire de téléchargement FTP."; // afficher l'erreur et les solutions...
    addForm(); // renvoyer au formulaire récemment rempli.
}
else {
    // continuer le traitement de la page...
}

2 votes

Cela fonctionne lorsque track_errors = Off, éventuellement aussi display_errors = Off et display_startup_errors = Off dans php.ini. Sinon, PHP n'arrivera même pas à ce point 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.

2 votes

Cette idée astucieuse fonctionne bien dans mes tests (PHP/5.5.8). Elle peut également être améliorée en prenant en compte $_SERVER['CONTENT_LENGTH'] et upload_max_filesize.

0 votes

Faisant suite au commentaire de raoulsson, y a-t-il un moyen de supprimer l'avertissement si vous n'êtes pas dans un environnement avec les erreurs supprimées?

7voto

staabm Points 395

Nous avons rencontré un problème avec les requêtes SOAP où une vérification de l'existence de $_POST et $_FILES ne fonctionne pas, car ils sont également vides sur des requêtes valides.

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

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

    if ($_SERVER['CONTENT_LENGTH'] > $maxPostSize) {
        throw new Exception(
            sprintf('Taille de post maximale dépassée ! Reçu %s octets, mais la limite est de %s octets.',
                $_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) {
        // Le modificateur 'G' est disponible depuis PHP 5.1.0
        case 'g':
            $val *= 1024;
            // continuer
        case 'm':
            $val *= 1024;
            // continuer
        case 'k':
            $val *= 1024;
            // continuer
    }

    return $val;
}

0 votes

@manuel-azar : les déclarations "break" ajoutées ne sont pas correctes. Ils ne doivent pas être là. Voir 3v4l.org/ABfGs

0 votes

Il est inutile de vérifier si la taille est dépassée. Il n'y aurait pas de content_length s'il n'y avait pas de fichier. Ainsi, il suffit de vérifier si la content_length est définie et si les variables post et fichier sont vides. Non ?

0 votes

Les transferts chunked n'ont pas d'en-tête Content-Length donc la vérification de cet en-tête ne vous aidera pas dans ces cas.

4voto

Building on @Matt McCormick's and @AbdullahAJM's answers, here is a PHP test case that checks the variables used in the test are set and then checks if the $_SERVER['CONTENT_LENGTH'] exceeds the php_max_filesize setting:

            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 "" .
                        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
                        ) .
                        '' .
                        __( 'Try increasing the post_max_size setting in your php.ini file.' , 'csa-slplus' ) .
                        '';
                }
            }

2voto

Doglas Points 315

C'est une façon simple de résoudre ce problème :

Il suffit d'appeler "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)//si c'est une requête post et que la variable $_POST est vide (symptôme de "erreur de taille maximale du post")
        ) {
            $max = get_ini_bytes('post_max_size');//obtenir la limite de la taille du post
            $send = $_SERVER['CONTENT_LENGTH'];//obtenir la taille du post envoyé

            if($max < $_SERVER['CONTENT_LENGTH'])//comparer
                throw new Exception(
                    'Taille maximale dépassée ! Ont été envoyés ' . 
                        number_format($send/(1024*1024), 2) . 'Mo, mais ' . number_format($max/(1024*1024), 2) . 'Mo est la limite de l'application.'
                    );
        }
    }

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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X