Як витончено обробляти файли, які перевищують 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

@Matt McCormick - вхід MAX_FILE_SIZE чудово працює - якщо розмір файлу перевищує цей, розмір файлу тепер відображається як 0, що є випадком, з яким я вже працював. Незважаючи на те, що це може обійти зловмисний користувач, це слугує моїм цілям тут, тому що я просто намагаюся граціозно вийти з ладу для звичайних користувачів.
Натан Лонг

Відповіді:


55

З документації :

Якщо розмір даних повідомлення перевищує post_max_size, суперглобалі $ _POST та $ _FILES порожні . Це можна відстежувати різними способами, наприклад, передавши змінну $ _GET скрипту, який обробляє дані, тобто <form action = "edit.php? Processing = 1">, а потім перевіривши, чи $ _GET ['обработан'] встановити.

Тож, на жаль, схоже, PHP надсилає помилку. І оскільки він надсилає порожній масив $ _POST, саме тому ваш скрипт повертається до порожньої форми - він не вважає, що це POST. (Досить погане дизайнерське рішення IMHO)

Цей коментатор також має цікаву ідею.

Здається, більш елегантним способом є порівняння між post_max_size та $ _SERVER ['CONTENT_LENGTH']. Зверніть увагу, що останній включає не тільки розмір завантаженого файлу та дані публікації, але також багаточастинні послідовності.


3
Будь ласка, прочитайте жирну частину. Тобто, якщо розмір файлу перевищує upload_max_filesize, користувач запитує, що відбувається, коли форма перевищує post_max_size. post_max_size слід встановити вище, ніж upload_max_filesize, щоб спробувати уникнути цієї проблеми, але OP може мати причини, щоб зберегти її незмінною.
Matt McCormick

1
На жаль, я змішав post_max_size і upload_max_filesize. +1
Пекка

@Matt - post_max_size IS встановлений вище, ніж upload_max_filesize, але я все одно отримую помилку, якщо завантаження перевищує обидва. Якщо він потрапляє між двома, я бачу, що розмір файлу показує 0.
Натан Лонг

1
Ви вирішили це, але так post_max_size доступний. Просто виконайте ini_get ('post_max_size'). ini_get () також можна використовувати для перевірки інших налаштувань INI.
Matt McCormick

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, тому це чудово працює.
raoulsson

2
Ця акуратна ідея добре працює в моїх тестах (PHP / 5.5.8). Вона також може бути поліпшена фіксація $_SERVER['CONTENT_LENGTH']і upload_max_filesizeдо уваги.
Альваро Гонсалес,

Ведучий на з raoulsson «s коментар, чи є спосіб придушити попередження , якщо ви не в середовищі з помилками пригнічені?
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;
}

@ 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.