Jak z wdziękiem obsługiwać pliki, które przekraczają wartość „post_max_size” PHP?

86

Pracuję nad formularzem PHP, który dołącza plik do wiadomości e-mail i próbuję z wdziękiem obsługiwać przypadki, w których przesłany plik jest zbyt duży.

Dowiedziałem się, że są dwa ustawienia, php.iniktóre wpływają na maksymalny rozmiar przesyłanego pliku: upload_max_filesizei post_max_size.

Jeśli rozmiar pliku przekracza upload_max_filesize, PHP zwraca rozmiar pliku jako 0. W porządku; Mogę to sprawdzić.

Ale jeśli przekroczy post_max_size, mój skrypt zawiedzie po cichu i wróci do pustej formy.

Czy jest jakiś sposób, aby złapać ten błąd?

Nathan Long
źródło
1
Czy masz dostęp do php.ini? Post_max_size powinien być większy niż upload_max_filesize. Powinieneś także użyć <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> w formularzu, jak opisano ca2.php.net/manual/en/features.file-upload.post-method.php
Matt McCormick
@Matt McCormick - wejście MAX_FILE_SIZE działa świetnie - jeśli rozmiar pliku przekracza ten rozmiar, rozmiar pliku jest teraz wyświetlany jako 0, co jest przypadkiem, który już załatwiłem. Mimo że może to zostać ominięte przez złośliwego użytkownika, służy to moim celom, ponieważ po prostu próbuję z wdziękiem zawieść zwykłym użytkownikom.
Nathan Long

Odpowiedzi:

55

Z dokumentacji :

Jeśli rozmiar danych post jest większy niż post_max_size, superglobalne $ _POST i $ _FILES są puste . Można to śledzić na różne sposoby, np. Przekazując zmienną $ _GET do skryptu przetwarzającego dane, tj. <Form action = "edit.php? Processing = 1">, a następnie sprawdzając, czy $ _GET ['processing'] jest zestaw.

Więc niestety nie wygląda na to, że PHP wysyła błąd. A ponieważ wysyła pustą tablicę $ _POST, dlatego twój skrypt wraca do pustego formularza - nie uważa, że ​​jest to POST. (Dość zła decyzja projektowa IMHO)

Ten komentator też ma ciekawy pomysł.

Wydaje się, że bardziej eleganckim sposobem jest porównanie między post_max_size i $ _SERVER ['CONTENT_LENGTH']. Należy pamiętać, że ta ostatnia obejmuje nie tylko rozmiar przesłanego pliku i dane postu, ale także sekwencje wieloczęściowe.

Matt McCormick
źródło
3
Przeczytaj pogrubioną część. Oznacza to, że jeśli rozmiar pliku przekracza upload_max_filesize, użytkownik pyta, co się stanie, gdy formularz przekroczy post_max_size. Post_max_size powinien być ustawiony wyżej niż upload_max_filesize, aby spróbować uniknąć tego problemu, ale OP może mieć powody, by zachować to samo.
Matt McCormick,
1
Przepraszam, pomyliłem post_max_size i upload_max_filesize. +1
Pekka
@Matt - post_max_size JEST ustawiony wyżej niż upload_max_filesize, ale nadal otrzymuję błąd, jeśli przesyłanie przekracza oba. Jeśli mieści się między nimi, widzę, że rozmiar pliku wynosi 0.
Nathan Long
1
Rozwiązałeś to, ale tak, post_max_size jest dostępny. Po prostu wykonaj ini_get ('post_max_size'). ini_get () może być również użyty do sprawdzenia innych ustawień INI.
Matt McCormick,
1
@SavasVedova - dziękuję za zwrócenie uwagi, ale nie mogę już rozwiązać problemu. Minęło już kilka lat i nie pracuję już w tej firmie ani w PHP. :)
Nathan Long
45

istnieje sposób na przechwycenie / obsługę plików przekraczających maksymalny rozmiar posta, jest to mój preferowany sposób, ponieważ informuje użytkownika końcowego, co się stało i kto jest winny;)

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
źródło
2
Działa to, gdy track_errors = Off, prawdopodobnie również display_errors = Off i display_startup_errors = Off w php.ini. W przeciwnym razie PHP nawet nie zajdzie tak daleko i nie wyśle ​​ostrzeżenia, jak w tytule pytania. Ale w systemie produkcyjnym powinno to być ustawienie php.ini, więc działa świetnie.
raoulsson
2
Ten fajny pomysł działa dobrze w moich testach (PHP / 5.5.8). Można go również ulepszyć biorąc $_SERVER['CONTENT_LENGTH']i biorąc upload_max_filesizepod uwagę.
Álvaro González
Wychodząc z komentarza Raoulssona , czy jest jakiś sposób na powstrzymanie ostrzeżenia, jeśli nie znajdujesz się w środowisku z wyłączonymi błędami?
brendo
6

Mamy problem z żądaniami SOAP, w których sprawdzanie pustości $ _POST i $ _FILES nie działa, ponieważ są one również puste w prawidłowych żądaniach.

Dlatego wdrożyliśmy sprawdzanie, porównując CONTENT_LENGTH i post_max_size. Zgłoszony wyjątek jest później przekształcany w XML-SOAP-FAULT przez naszą zarejestrowaną procedurę obsługi wyjątków.

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
źródło
@ manuel-azar: dodane zwroty „break” nie są poprawne. te nie należą do tego miejsca .. zobacz 3v4l.org/ABfGs
staabm
Nie trzeba sprawdzać, czy rozmiar przekracza. Nie byłoby content_length, gdyby nie było pliku. Musisz więc tylko sprawdzić, czy długość_treści jest ustawiona, a zmienne post i plik są puste. Nie?
ADJenks
4

Opierając się na odpowiedziach @Matt McCormick i @ AbdullahAJM, oto przypadek testowy PHP, który sprawdza, czy zmienne użyte w teście są ustawione, a następnie sprawdza, czy $ _SERVER ['CONTENT_LENGTH'] przekracza ustawienie 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
źródło
To najbardziej logiczny sposób, aby to zrobić. Po prostu sprawdź, czy _post jest pusty, ale długość treści nie. Nie ma potrzeby porównywania rozmiarów.
ADJenks
1

To prosty sposób na rozwiązanie tego problemu:

Po prostu wywołaj „checkPostSizeExceeded” na początku kodu

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

Pamiętaj, skopiuj tę pomocniczą funkcję:

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
źródło