Come gestire con grazia i file che superano `post_max_size` di PHP?


86

Sto lavorando a un modulo PHP che allega un file a un'e-mail e cerco di gestire con garbo i casi in cui il file caricato è troppo grande.

Ho imparato che ci sono due impostazioni php.iniche influenzano la dimensione massima di un caricamento di file: upload_max_filesizee post_max_size.

Se la dimensione di un file supera upload_max_filesize, PHP restituisce la dimensione del file come 0. Va bene; Posso verificarlo.

Ma se supera post_max_size, il mio script fallisce silenziosamente e torna al modulo vuoto.

C'è un modo per rilevare questo errore?


1
Hai accesso a php.ini? post_max_size dovrebbe essere impostato su un valore maggiore di upload_max_filesize. Dovresti anche utilizzare <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> nel modulo come descritto ca2.php.net/manual/en/features.file-upload.post-method.php
Matt McCormick

@ Matt McCormick - l'input MAX_FILE_SIZE funziona alla grande - se la dimensione del file supera quella, la dimensione del file ora mostra 0, che è un caso che ho già gestito. Anche se questo può essere aggirato da un utente malintenzionato, serve ai miei scopi qui, perché sto solo cercando di fallire con grazia per gli utenti normali.
Nathan Long,

Risposte:


55

Dalla documentazione :

Se la dimensione dei dati del post è maggiore di post_max_size, i superglobali $ _POST e $ _FILES sono vuoti . Questo può essere monitorato in vari modi, ad esempio passando la variabile $ _GET allo script che elabora i dati, ad esempio <form action = "edit.php? Processing = 1">, e poi controllando se $ _GET ['elaborato'] è impostato.

Quindi, sfortunatamente, non sembra che PHP invii un errore. E poiché invia un array $ _POST vuoto, è per questo che lo script torna alla forma vuota - non pensa che sia un POST. (Piuttosto una cattiva decisione di progettazione IMHO)

Anche questo commentatore ha un'idea interessante.

Sembra che un modo più elegante sia il confronto tra post_max_size e $ _SERVER ['CONTENT_LENGTH']. Si noti che quest'ultimo include non solo la dimensione del file caricato più i dati del post, ma anche le sequenze multiparte.


3
Si prega di leggere la parte in grassetto. Ciò significa che se la dimensione del file supera upload_max_filesize, l'utente chiede cosa succede quando il modulo supera post_max_size. post_max_size dovrebbe essere impostato su un valore superiore a upload_max_filesize per cercare di evitare questo problema, ma l'OP potrebbe avere ragioni per mantenerlo lo stesso.
Matt McCormick

1
Scusa, ho confuso post_max_size e upload_max_filesize. +1
Pekka

@Matt - post_max_size È impostato su un valore superiore a upload_max_filesize, ma ricevo ancora l'errore se il caricamento supera entrambi. Se cade tra i due, vedo che la dimensione del file mostra 0.
Nathan Long

1
L'hai risolto, ma sì, post_max_size è disponibile. Basta fare ini_get ('post_max_size'). ini_get () può essere utilizzato anche per controllare altre impostazioni INI.
Matt McCormick

1
@ SavasVedova - grazie per averlo sottolineato, ma non posso più risolvere i problemi. Sono passati alcuni anni e non lavoro più in quell'azienda o in PHP. :)
Nathan Long

45

c'è un modo per catturare / gestire i file che superano la dimensione massima del post, questo è il mio preferito in quanto dice all'utente finale cosa è successo e chi è la colpa;)

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

2
Funziona quando track_errors = Off, possibilmente anche display_errors = Off e display_startup_errors = Off in php.ini. Altrimenti PHP non arriverà nemmeno a tanto e invierà l'avviso, come nel titolo della domanda. Ma in un sistema di produzione questa dovrebbe essere l'impostazione php.ini, quindi funziona alla grande.
raoulsson

2
Questa bella idea funziona bene nei miei test (PHP / 5.5.8). Si può anche essere migliorata presa $_SERVER['CONTENT_LENGTH']e upload_max_filesizein considerazione.
Álvaro González

Partendo dal commento di raoulsson , c'è un modo per sopprimere l'avviso se non ci si trova in un ambiente con errori soppressi?
brendo

6

Abbiamo riscontrato il problema per le richieste SOAP in cui un controllo per vuoto di $ _POST e $ _FILES non funziona, perché sono vuoti anche su richieste valide.

Pertanto abbiamo implementato un controllo, confrontando CONTENT_LENGTH e post_max_size. L'eccezione generata viene successivamente trasformata in XML-SOAP-FAULT dal nostro gestore di eccezioni registrato.

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

@ manuel-azar: le fasi di "pausa" aggiunte non sono corrette. quelli non appartengono lì .. vedi 3v4l.org/ABfGs
staabm

Non è necessario controllare il controllo se la dimensione supera. Non ci sarebbe content_length se non ci fosse alcun file. Quindi devi solo verificare se content_length è impostato e le variabili di post e file sono vuote. No?
ADJenks

4

Basandosi sulle risposte di @Matt McCormick e @ AbdullahAJM, ecco un test case PHP che controlla che le variabili utilizzate nel test siano impostate e quindi controlla se $ _SERVER ['CONTENT_LENGTH'] supera l'impostazione 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>';
                }
            }

Questo è il modo più logico per farlo. Controlla se _post è vuoto ma la lunghezza del contenuto non lo è. Non c'è bisogno di confrontare le taglie.
ADJenks

1

Questo è un modo semplice per risolvere questo problema:

Basta chiamare "checkPostSizeExceeded" all'inizio del codice

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.'
                    );
        }
    }

Ricorda di copiare questa funzione ausiliaria:

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.