วิธีจัดการไฟล์ที่มีขนาดเกิน "post_max_size" ของ PHP อย่างสง่างาม


86

ฉันกำลังทำงานกับรูปแบบ PHP ที่แนบไฟล์กับอีเมลและพยายามจัดการกรณีที่ไฟล์ที่อัปโหลดมีขนาดใหญ่เกินไป

ฉันได้เรียนรู้ว่ามีการตั้งค่าสองอย่างphp.iniที่ส่งผลต่อขนาดสูงสุดของการอัปโหลดไฟล์: upload_max_filesizeและpost_max_size.

หากไฟล์มีขนาดเกินupload_max_filesizePHP จะคืนขนาดไฟล์เป็น 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 ซึ่งเป็นกรณีที่ฉันได้รับการจัดการแล้ว แม้ว่าสิ่งนี้จะสามารถข้ามได้โดยผู้ใช้ที่เป็นอันตราย แต่ก็ตอบสนองจุดประสงค์ของฉันที่นี่เพราะฉันแค่พยายามล้มเหลวอย่างสง่างามสำหรับผู้ใช้ทั่วไป
Nathan Long

คำตอบ:


55

จากเอกสารประกอบ :

ถ้าขนาดของข้อมูลที่โพสต์มากกว่า post_max_size ที่$ _POST และ $ _FILES superglobals จะว่างเปล่า ซึ่งสามารถติดตามได้หลายวิธีเช่นโดยการส่งตัวแปร $ _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
Pekka

@Matt - post_max_size ตั้งไว้สูงกว่า upload_max_filesize แต่ฉันยังคงได้รับความล้มเหลวหากการอัปโหลดเกินทั้งสองอย่าง ถ้ามันอยู่ระหว่างทั้งสองฉันเห็นว่าขนาดไฟล์แสดงเป็น 0
Nathan Long

1
คุณได้แก้ไขแล้ว แต่ใช่ post_max_size สามารถใช้ได้ เพียงแค่ทำ ini_get ('post_max_size') ini_get () ยังสามารถใช้เพื่อตรวจสอบการตั้งค่า INI อื่น ๆ
Matt McCormick

1
@SavasVedova - ขอบคุณที่ชี้ให้เห็น แต่ฉันไม่สามารถแก้ไขปัญหาได้อีกต่อไป เป็นเวลาสองสามปีแล้วที่ฉันไม่ได้ทำงานที่ บริษัท นั้นหรือใน PHP อีกต่อไป :)
Nathan Long

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 = ปิดอาจจะยัง display_errors = ปิดและ display_startup_errors = ปิดใน php.ini ไม่เช่นนั้น PHP จะไปไม่ถึงจุดนั้นและส่งคำเตือนเหมือนในหัวข้อคำถาม แต่ในระบบการใช้งานจริงควรเป็นการตั้งค่า php.ini ซึ่งจะใช้งานได้ดี
raoulsson

2
ความคิดที่เรียบร้อยนี้ใช้งานได้ดีในการทดสอบของฉัน (PHP / 5.5.8) นอกจากนี้ยังสามารถเพิ่มการซัก$_SERVER['CONTENT_LENGTH']และupload_max_filesizeเข้าบัญชี
ÁlvaroGonzález

จากความคิดเห็นของraoulssonมีวิธีใดบ้างในการระงับคำเตือนหากคุณไม่ได้อยู่ในสภาพแวดล้อมที่มีข้อผิดพลาดถูกระงับ
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: การเพิ่ม "break" stements ไม่ถูกต้อง เหล่านั้นไม่ได้อยู่ที่นั่น .. ดู3v4l.org/ABfGs
staabm

ไม่จำเป็นต้องตรวจสอบว่าขนาดเกินหรือไม่ จะไม่มี content_length หากไม่มีไฟล์ ดังนั้นคุณต้องตรวจสอบว่ามีการตั้งค่า content_length และตัวแปรของโพสต์และไฟล์ว่างเปล่า ไม่?
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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.