Làm thế nào để xử lý một cách duyên dáng các tệp vượt quá `post_max_size` của PHP?


86

Tôi đang làm việc trên một biểu mẫu PHP để đính kèm một tệp vào email và cố gắng xử lý một cách khéo léo các trường hợp tệp được tải lên quá lớn.

Tôi đã biết rằng có hai cài đặt php.iniảnh hưởng đến kích thước tối đa của tệp tải lên: upload_max_filesizepost_max_size.

Nếu kích thước của tệp vượt quá upload_max_filesize, PHP trả về kích thước của tệp là 0. Điều đó tốt; Tôi có thể kiểm tra điều đó.

Nhưng nếu nó vượt quá post_max_size, tập lệnh của tôi sẽ không thành công và trở lại dạng trống.

Có cách nào để bắt lỗi này không?


1
Bạn có quyền truy cập vào php.ini không? post_max_size phải được đặt lớn hơn upload_max_filesize. Bạn cũng nên sử dụng <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> trên biểu mẫu như đã nêu ca2.php.net/manual/en/features.file-upload.post-method.php
Matt McCormick

@Matt McCormick - đầu vào MAX_FILE_SIZE hoạt động tốt - nếu kích thước tệp vượt quá kích thước đó, kích thước tệp hiện hiển thị là 0, đây là trường hợp tôi đã xử lý. Mặc dù điều này có thể bị người dùng độc hại bỏ qua, nhưng nó phục vụ mục đích của tôi ở đây, bởi vì tôi chỉ cố gắng thất bại một cách duyên dáng đối với người dùng thông thường.
Nathan Long

Câu trả lời:


55

Từ tài liệu :

Nếu kích thước của dữ liệu bài đăng lớn hơn post_max_size, thì siêu cầu nối $ _POST và $ _FILES sẽ trống . Điều này có thể được theo dõi theo nhiều cách khác nhau, ví dụ: bằng cách chuyển biến $ _GET đến tập lệnh xử lý dữ liệu, tức là <form action = "edit.php? Xử lý = 1">, rồi kiểm tra xem $ _GET ['đã xử lý'] bộ.

Vì vậy, thật không may, có vẻ như PHP không gửi lỗi. Và vì nó gửi mảng $ _POST trống, đó là lý do tại sao tập lệnh của bạn trở về dạng trống - nó không nghĩ đó là một ĐĂNG. (Khá là một quyết định thiết kế sai lầm IMHO)

Người bình luận này cũng có một ý tưởng thú vị.

Có vẻ như một cách thanh lịch hơn là so sánh giữa post_max_size và $ _SERVER ['CONTENT_LENGTH']. Xin lưu ý rằng phần sau không chỉ bao gồm kích thước của tệp tải lên cộng với dữ liệu bài đăng mà còn bao gồm các chuỗi nhiều phần.


3
Vui lòng đọc phần in đậm. Đó là nếu kích thước tệp vượt quá upload_max_filesize, người dùng sẽ hỏi điều gì sẽ xảy ra khi biểu mẫu vượt quá post_max_size. post_max_size nên được đặt cao hơn upload_max_filesize để tránh sự cố này nhưng OP có thể có lý do để giữ nguyên.
Matt McCormick

1
Xin lỗi, tôi đã trộn lẫn post_max_size và upload_max_filesize. +1
Pekka

@Matt - post_max_size IS đặt cao hơn upload_max_filesize, nhưng tôi vẫn gặp lỗi nếu tải lên vượt quá cả hai. Nếu nó rơi giữa hai, tôi thấy rằng các chương trình kích thước tập tin 0.
Nathan dài

1
Bạn đã giải quyết được nó, nhưng có post_max_size. Chỉ làm ini_get ('post_max_size'). ini_get () cũng có thể được sử dụng để kiểm tra các cài đặt INI khác.
Matt McCormick

1
@SavasVedova - cảm ơn bạn đã chỉ ra điều đó, nhưng tôi không thể khắc phục sự cố được nữa. Đã vài năm rồi và tôi không còn làm việc ở công ty đó hoặc ở PHP nữa. :)
Nathan Long

45

có một cách để bắt / xử lý các tệp vượt quá kích thước bài đăng tối đa, đây là cách tôi ưa thích hơn, vì nó cho người dùng cuối biết điều gì đã xảy ra và ai là người có lỗi;)

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
Điều này hoạt động khi track_errors = Tắt, cũng có thể là display_errors = Tắt và display_startup_errors = Tắt trong php.ini. Nếu không thì PHP thậm chí sẽ không đi được xa như vậy và gửi cảnh báo, như trong tiêu đề câu hỏi. Nhưng trong hệ thống sản xuất, đây phải là cài đặt php.ini, vì vậy điều này hoạt động tốt.
raoulsson

2
Ý tưởng gọn gàng này đang hoạt động tốt trong các thử nghiệm của tôi (PHP / 5.5.8). Nó cũng có thể được nâng cao tính đến $_SERVER['CONTENT_LENGTH']và tính upload_max_filesizeđến.
Álvaro González

Dẫn đầu từ bình luận của raoulsson , có cách nào để ngăn chặn cảnh báo nếu bạn không ở trong môi trường có lỗi bị triệt tiêu không?
brendo

6

Chúng tôi gặp sự cố đối với các yêu cầu SOAP trong đó kiểm tra độ trống của $ _POST và $ _FILES không hoạt động vì chúng cũng trống đối với các yêu cầu hợp lệ.

Do đó, chúng tôi đã triển khai kiểm tra, so sánh CONTENT_LENGTH và post_max_size. Ngoại lệ được ném sau đó được chuyển đổi thành XML-SOAP-FAULT bởi trình xử lý ngoại lệ đã đăng ký của chúng tôi.

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: các bước "ngắt" được thêm vào là không đúng. những thứ đó không thuộc về đó .. xem 3v4l.org/ABfGs
staabm 23/02/17

Không cần thiết phải kiểm tra kiểm tra nếu kích thước vượt quá. Sẽ không có content_length nếu không có tệp. Vì vậy, bạn chỉ cần kiểm tra xem liệu content_length đã được đặt chưa và các biến bài đăng và tệp có trống không. Không?
ADJenks

4

Dựa trên câu trả lời của @Matt McCormick và @ AbdullahAJM, đây là một trường hợp thử nghiệm PHP để kiểm tra các biến được sử dụng trong thử nghiệm đã được đặt chưa và sau đó kiểm tra xem $ _SERVER ['CONTENT_LENGTH'] có vượt quá cài đặt php_max_filesize hay không:

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

Đây là cách hợp lý nhất để làm điều đó. Chỉ cần kiểm tra xem _post có trống nhưng độ dài nội dung thì không. Không cần so sánh kích thước.
ADJenks

1

Đó là một cách đơn giản để khắc phục sự cố này:

Chỉ cần gọi "checkPostSizeExceeded" khi bắt đầu mã của bạn

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

Hãy nhớ sao chép chức năng hỗ trợ này:

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.