如何优雅地处理超过PHP的`post_max_size`的文件?


86

我正在处理一个将文件附加到电子邮件的PHP表单,并试图妥善处理上传的文件太大的情况。

我了解到,其中有两个设置php.ini会影响文件上传的最大大小:upload_max_filesizepost_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,这是我已经处理过的情况。即使恶意用户可以绕过此漏洞,也可以满足我的目的,因为我只是在尝试让普通用户正常失败。
内森·朗2010年

Answers:


55

文档中

如果发布数据的大小大于post_max_size,则$ _POST和$ _FILES超全局变量为空。这可以通过各种方式进行跟踪,例如,将$ _GET变量传递给处理数据的脚本,即<form action =“ edit.php?processed = 1”>,然后检查$ _GET ['processed']是否为组。

因此,不幸的是,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 2010年

@Matt-post_max_size设置为高于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
当php.ini中的track_errors = Off,也可能是display_errors = Off和display_startup_errors = Off时,此方法有效。否则,PHP甚至都不会发出警告,就像问题标题中一样。但是在生产系统中,这应该是php.ini设置,因此效果很好。
raoulsson

2
在我的测试(PHP / 5.5.8)中,这个简洁的想法很好用。它也可以提高拍摄$_SERVER['CONTENT_LENGTH']upload_max_filesize考虑。
阿尔瓦罗·冈萨雷斯

raoulsson的评论开始,如果您不在错误被抑制的环境中,有什么方法可以抑制警告?
brendo 2014年

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”语句是不正确的。那些不属于那里的..见3v4l.org/ABfGs
staabm

如果尺寸超过了,则不必检查。如果没有文件,则没有content_length。因此,您只需要检查是否设置了content_length以及post和file变量为空。没有?
ADJenks '19

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 '19

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.