Как изящно обрабатывать файлы, размер которых превышает PHP post_max_size?

86

Я работаю над формой PHP, которая прикрепляет файл к электронному письму, и пытаюсь изящно обрабатывать случаи, когда загруженный файл слишком велик.

Я узнал, что есть две настройки, php.iniкоторые влияют на максимальный размер загружаемого файла: upload_max_filesizeи post_max_size.

Если размер файла превышает его upload_max_filesize, PHP возвращает размер файла как 0. Это нормально; Я могу проверить это.

Но если он превышает post_max_size, мой сценарий молча терпит неудачу и возвращается к пустой форме.

Есть ли способ отловить эту ошибку?

Натан Лонг
источник
1
У вас есть доступ к php.ini? post_max_size следует установить больше, чем upload_max_filesize. Вы также должны использовать <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> в форме, как указано ca2.php.net/manual/en/features.file-upload.post-method.php
Мэтт Маккормик
@Matt McCormick - ввод MAX_FILE_SIZE отлично работает - если размер файла превышает это значение, размер файла теперь отображается как 0, что является случаем, с которым я уже работал. Несмотря на то, что злоумышленник может обойти это, здесь это служит моим целям, потому что я просто пытаюсь изящно терпеть неудачу для обычных пользователей.
Натан Лонг

Ответы:

55

Из документации :

Если размер пост-данных больше post_max_size, суперглобальные переменные $ _POST и $ _FILES пусты . Это можно отследить различными способами, например, передав переменную $ _GET в сценарий, обрабатывающий данные, т.е. <form action = "edit.php? Processing = 1">, а затем проверяя, является ли $ _GET ['Processing'] задавать.

Так что, к сожалению, не похоже, что PHP отправляет ошибку. И поскольку он отправляет пустой массив $ _POST, поэтому ваш скрипт возвращается к пустой форме - он не думает, что это POST. (Довольно неудачное дизайнерское решение ИМХО)

У этого комментатора тоже есть интересная идея.

Кажется, более элегантным способом является сравнение post_max_size и $ _SERVER ['CONTENT_LENGTH']. Обратите внимание, что последний включает не только размер загруженного файла и данных публикации, но и составные последовательности.

Мэтт Маккормик
источник
3
Пожалуйста, прочтите выделенную жирным шрифтом часть. То есть, если размер файла превышает upload_max_filesize, пользователь спрашивает, что происходит, когда форма превышает post_max_size. post_max_size следует установить выше, чем upload_max_filesize, чтобы попытаться избежать этой проблемы, но у OP могут быть причины для его сохранения.
Мэтт Маккормик
1
Извините, перепутал post_max_size и upload_max_filesize. +1
Pekka
@Matt - post_max_size IS установлено выше, чем upload_max_filesize, но я все равно получаю ошибку, если загрузка превышает оба значения. Если он находится между двумя, я вижу, что размер файла показывает 0.
Натан Лонг
1
Вы решили это, но да, post_max_size доступен. Просто сделайте ini_get ('post_max_size'). ini_get () также можно использовать для проверки других настроек INI.
Мэтт Маккормик
1
@SavasVedova - спасибо, что указали на это, но я больше не могу устранять неполадки. Прошло несколько лет, и я больше не работаю в этой компании или в PHP. :)
Натан Лонг
45

есть способ поймать / обработать файлы, превышающие максимальный размер публикации, это мой предпочтительный вариант, поскольку он сообщает конечному пользователю, что произошло и кто виноват;)

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
Это работает, когда track_errors = Off, возможно также display_errors = Off и display_startup_errors = Off в php.ini. В противном случае PHP даже не зайдет так далеко и отправит предупреждение, как в заголовке вопроса. Но в производственной системе это должен быть параметр php.ini, поэтому он отлично работает.
Раульссон
2
Эта изящная идея отлично работает в моих тестах (PHP / 5.5.8). Она также может быть улучшена фиксация $_SERVER['CONTENT_LENGTH']и upload_max_filesizeво внимание.
Álvaro González
Исходя из комментария Раулссона , есть ли способ подавить предупреждение, если вы не находитесь в среде с подавленными ошибками?
brendo
6

У нас возникла проблема для запросов SOAP, когда проверка на пустоту $ _POST и $ _FILES не работает, потому что они также пусты для действительных запросов.

Поэтому мы реализовали проверку, сравнивая CONTENT_LENGTH и post_max_size. Выброшенное исключение позже преобразуется в XML-SOAP-FAULT нашим зарегистрированным обработчиком исключений.

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
источник
@ manuel-azar: добавленные шаги «перерыва» неверны. им там не место .. см. 3v4l.org/ABfGs
staabm
Нет необходимости проверять чек, если размер превышает. Если бы не было файла, не было бы content_length. Поэтому вам просто нужно проверить, установлен ли content_length, а переменные post и file пусты. Нет?
ADJenks
4

Основываясь на ответах @Matt McCormick и @ AbdullahAJM, вот тестовый пример PHP, который проверяет, установлены ли переменные, используемые в тесте, а затем проверяет, превышает ли $ _SERVER ['CONTENT_LENGTH'] параметр 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>';
                }
            }
Лэнс Кливленд
источник
Это наиболее логичный способ сделать это. Просто проверьте, пуст ли _post, а длина содержимого - нет. Сравнивать размеры не нужно.
ADJenks
1

Это простой способ решить эту проблему:

Просто вызовите checkPostSizeExceeded в начале кода.

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

Не забудьте скопировать эту вспомогательную функцию:

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;
}
Доглас
источник