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

Posted

技术标签:

【中文标题】如何优雅地处理超过 PHP `post_max_size` 的文件?【英文标题】:How to gracefully handle files that exceed PHP's `post_max_size`? 【发布时间】:2011-01-09 04:07:13 【问题描述】:

我正在开发一个将文件附加到电子邮件的 php 表单,并试图优雅地处理上传文件太大的情况。

我了解到php.ini 中有两个设置会影响文件上传的最大大小:upload_max_filesizepost_max_size

如果文件大小超过upload_max_filesize,PHP 将文件大小返回为 0。没关系;我可以检查一下。

但如果超过post_max_size,我的脚本会静默失败并返回空白表单。

有什么办法可以捕捉到这个错误?

【问题讨论】:

您可以访问 php.ini 吗? post_max_size 应设置为大于upload_max_filesize。您还应该在ca2.php.net/manual/en/features.file-upload.post-method.php 中概述的表单上使用 @Matt McCormick - MAX_FILE_SIZE 输入效果很好 - 如果文件大小超过该值,文件大小现在显示为 0,这是我已经处理过的情况。尽管这可以被恶意用户绕过,但它在这里符合我的目的,因为我只是试图为普通用户优雅地失败。 【参考方案1】:

来自the documentation:

如果发布数据的大小更大 比 post_max_size、$_POST 和 $_FILES 超全局变量为空。这 可以通过各种方式进行跟踪,例如 通过将 $_GET 变量传递给 处理数据的脚本,即

,和 然后检查 $_GET['processed'] 是否 设置。

所以不幸的是,PHP 看起来并没有发送错误。而且由于它发送的是空的 $_POST 数组,这就是您的脚本返回空白表单的原因 - 它认为它不是 POST。 (相当糟糕的设计决策恕我直言)

This commenter 也有一个有趣的想法。

似乎更优雅的方式是 post_max_size 和之间的比较 $_SERVER['CONTENT_LENGTH']。请 请注意,后者不仅包括 上传文件的大小加上发布数据 还有多部分序列。

【讨论】:

请阅读粗体部分。也就是说,如果文件大小超过upload_max_filesize,用户会询问当表单超过post_max_size 时会发生什么。 post_max_size 应设置为高于 upload_max_filesize 以避免此问题,但 OP 可能有理由保持不变。 抱歉,我把 post_max_size 和 upload_max_filesize 弄混了。 +1 @Matt - post_max_size 设置为高于upload_max_filesize,但如果上传超过两者,我仍然会失败。如果介于两者之间,我会看到文件大小显示为 0。 你已经解决了,但是 post_max_size 是可用的。只需执行 ini_get('post_max_size')。 ini_get() 也可用于检查其他 INI 设置。 @SavasVedova - 感谢您指出这一点,但我无法再进行故障排除。已经有几年了,我不再在那家公司或 PHP 工作。 :)【参考方案2】:

有一种方法可以捕获/处理超过最大帖子大小的文件,这是我的首选,因为它告诉最终用户发生了什么以及谁有错;)

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...

【讨论】:

这在 track_errors = Off 时有效,也可能在 php.ini 中 display_errors = Off 和 display_startup_errors = Off。否则 PHP 甚至不会走那么远并发送警告,就像在问题标题中一样。但在生产系统中,这应该是 php.ini 设置,所以效果很好。 这个巧妙的想法在我的测试中运行良好 (PHP/5.5.8)。还可以考虑到$_SERVER['CONTENT_LENGTH']upload_max_filesize 来增强它。 从raoulsson 的评论开始,如果您不在一个错误被抑制的环境中,有什么方法可以抑制警告? 此检测有一些误报。超过最大输入时间或分块传输中的暂停时间过长也会出现空 POST 和 FILES 超全局变量。【参考方案3】:

我们遇到了 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(
            $valstrlen($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 大小是否超标无需检查。如果没有文件,就没有 content_length。因此,您只需检查是否设置了 content_length 并且 post 和 file 变量是否为空。没有? 分块传输没有 Content-Length 标头,因此在这些情况下对该标头的检查对您没有帮助。【参考方案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 是否为空,但内容长度不是。无需比较尺寸。【参考方案5】:

这是解决此问题的简单方法:

只需在代码开头调用“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_valuestrlen($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;

【讨论】:

以上是关于如何优雅地处理超过 PHP `post_max_size` 的文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在php中优雅的地调用python程序

SpringBoot:如何优雅地处理全局异常?

PHP如何优雅地在PHP里写注释 | PHP的注释PHPDoc

PHP如何优雅地在PHP里写注释 | PHP的注释PHPDoc

如何优雅地处理重复(并发)请求?

过时的实现如何优雅地处理新版本的 OpenType 字体?