PHP获取实际最大上传大小

69

使用时

ini_get("upload_max_filesize");

它实际上会给你在php.ini文件中指定的字符串。

不建议将此值用作最大上传大小的参考,因为:

  • 可以使用所谓的shorthandbytes,如1M等,需要大量的额外解析
  • 当upload_max_filesize为例如0.25M时,实际上是零,再次使该值的解析更加困难
  • 而且,如果该值包含任何空格,则php将其解释为零,而使用ini_get时显示没有空格的值

那么,有没有办法获取PHP实际使用的值,除了由ini_get报告的值,或者确定最佳方法是什么?


可能是如何确定PHP中的最大文件上传限制的重复问题。 - A.L
(int)ini_get("upload_max_filesize")*1024 将其转换为KB。例如,2M -> 2048。 - Afraz Ahmad
@AfrazAhmad 我觉得你误解了,upload_max_filesize设置可能不是以兆字节为单位的。 - Hayden Thring
@AfrazAhmad 我觉得你没有理解重点,upload_max_filesize设置可能不是以兆字节为单位的。 - undefined
6个回答

77

Drupal 已经相当优雅地实现了这一点:

// Returns a file size limit in bytes based on the PHP upload_max_filesize
// and post_max_size
function file_upload_max_size() {
  static $max_size = -1;

  if ($max_size < 0) {
    // Start with post_max_size.
    $post_max_size = parse_size(ini_get('post_max_size'));
    if ($post_max_size > 0) {
      $max_size = $post_max_size;
    }

    // If upload_max_size is less, then reduce. Except if upload_max_size is
    // zero, which indicates no limit.
    $upload_max = parse_size(ini_get('upload_max_filesize'));
    if ($upload_max > 0 && $upload_max < $max_size) {
      $max_size = $upload_max;
    }
  }
  return $max_size;
}

function parse_size($size) {
  $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
  $size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
  if ($unit) {
    // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
    return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
  }
  else {
    return round($size);
  }
}

以上功能可在 Drupal 的任何地方使用,或者您可以将其复制并在自己的项目中使用,但须遵守 GPL 许可协议第2版或更高版本的条款。

至于您提问的第二部分和第三部分,您需要直接解析 php.ini 文件。这些基本上是配置错误,而 PHP 正在采用后备行为。看起来您实际上可以在 PHP 中获取已加载的 php.ini 文件位置,尽管尝试从中读取可能无法与 basedir 或 safe-mode 启用一起工作:

$max_size = -1;
$post_overhead = 1024; // POST data contains more than just the file upload; see comment from @jlh
$files = array_merge(array(php_ini_loaded_file()), explode(",\n", php_ini_scanned_files()));
foreach (array_filter($files) as $file) {
  $ini = parse_ini_file($file);
  $regex = '/^([0-9]+)([bkmgtpezy])$/i';
  if (!empty($ini['post_max_size']) && preg_match($regex, $ini['post_max_size'], $match)) {
    $post_max_size = round($match[1] * pow(1024, stripos('bkmgtpezy', strtolower($match[2])));
    if ($post_max_size > 0) {
      $max_size = $post_max_size - $post_overhead;
    }
  }
  if (!empty($ini['upload_max_filesize']) && preg_match($regex, $ini['upload_max_filesize'], $match)) {
    $upload_max_filesize = round($match[1] * pow(1024, stripos('bkmgtpezy', strtolower($match[2])));
    if ($upload_max_filesize > 0 && ($max_size <= 0 || $max_size > $upload_max_filesize) {
      $max_size = $upload_max_filesize;
    }
  }
}

echo $max_size;

4
"pow"这行代码很优雅,但只解决了问题列表中的其中一个问题。 - Zulakis
1
目前来看,这似乎是我最好的选择。如果php.ini不可读,则回退到ini_get()。非常好。 - Zulakis
如果这个限制实际上被执行了,我认为它是由允许 PHP 运行的 Apache 或 CGI 控制器处理的。他们是如何处理的?DRY 原则建议我们应该找到将其转换为字节的内部机制,而不是膨胀我们自己的代码(假设我们还没有运行 Drupal)。 - Seldom 'Where's Monica' Needy
@SeldomNeedy:我会认为这是在mod_php中处理的。用户总是可以自由地发送任何垃圾数据,这只是在PHP放弃流并倒出其余部分之前的限制。至于DRY……好吧,这是PHP,所以我猜你可能可以通过Zend获取内部实现,但否则你和其他无法访问的内部状态一样运气不佳。 - meustrus
注意:自 PHP 5.3.2 / 5.2.12 起,post_max_size=0 也表示没有限制,但在我看来,上述代码将返回 0。如果 upload_max_filesize 的值不是 0,则应返回其值。 - Christopher K.
显示剩余5条评论

45

这里是完整的解决方案。它处理了所有陷阱,如缩写字节符号,并考虑了post_max_size:

/**
* This function returns the maximum files size that can be uploaded 
* in PHP
* @returns int File size in bytes
**/
function getMaximumFileUploadSize()  
{  
    return min(convertPHPSizeToBytes(ini_get('post_max_size')), convertPHPSizeToBytes(ini_get('upload_max_filesize')));  
}  

/**
* This function transforms the php.ini notation for numbers (like '2M') to an integer (2*1024*1024 in this case)
* 
* @param string $sSize
* @return integer The value in bytes
*/
function convertPHPSizeToBytes($sSize)
{
    //
    $sSuffix = strtoupper(substr($sSize, -1));
    if (!in_array($sSuffix,array('P','T','G','M','K'))){
        return (int)$sSize;  
    } 
    $iValue = substr($sSize, 0, -1);
    switch ($sSuffix) {
        case 'P':
            $iValue *= 1024;
            // Fallthrough intended
        case 'T':
            $iValue *= 1024;
            // Fallthrough intended
        case 'G':
            $iValue *= 1024;
            // Fallthrough intended
        case 'M':
            $iValue *= 1024;
            // Fallthrough intended
        case 'K':
            $iValue *= 1024;
            break;
    }
    return (int)$iValue;
}      

8
直到我最终认识到switch语句只有一个单一的“break”声明,并且“case”行的顺序很重要,我才明白这个是如何工作的(具体指switch语句)。它运行良好,但我担心一些初级程序员稍后要回来尝试修改这个代码。也许一个“更标准化”的版本会是一个更好的长期解决方案? - Gavin G
嗯,这并没有真正解决我在问题中提到的问题,因为它只是使用了ini_get() ;-) 我们已经有很多像这样的答案了。 - Zulakis
1
@GavinG,我现在已经按照PSR-2的要求添加了适当的注释,因此"fallthrough"很明显。 - Deckard
链接已失效... - catbadger
零和负一怎么办……如果将值设置为这些,意味着没有限制。 - Yasser CHENIK

10

这是我使用的:

function asBytes($ini_v) {
   $ini_v = trim($ini_v);
   $s = [ 'g'=> 1<<30, 'm' => 1<<20, 'k' => 1<<10 ];
   return intval($ini_v) * ($s[strtolower(substr($ini_v,-1))] ?: 1);
}

2
在2020年,我们可以使用kbs <?php function kbs($v){ return intval($v) * ([ 'p'=> 1<<40, 't' => 1<<30, 'g'=> 1<<20, 'm' => 1<<10, 'k' => 1 ][strtolower(substr(trim($v),-1))] ?: 1); } echo(kbs('1g')); ?> - user5781320
太喜欢了!我就是喜欢简短简单的代码!!! - Hayden Thring
太喜欢了!我就是喜欢简短简单的代码!!! - undefined

5
看起来这是不可能的。
因此,我将继续使用这段代码:
function convertBytes( $value ) {
    if ( is_numeric( $value ) ) {
        return $value;
    } else {
        $value_length = strlen($value);
        $qty = substr( $value, 0, $value_length - 1 );
        $unit = strtolower( substr( $value, $value_length - 1 ) );
        switch ( $unit ) {
            case 'k':
                $qty *= 1024;
                break;
            case 'm':
                $qty *= 1048576;
                break;
            case 'g':
                $qty *= 1073741824;
                break;
        }
        return $qty;
    }
}
$maxFileSize = convertBytes(ini_get('upload_max_filesize'));

这段话来自于这篇php.net评论。

仍然欢迎更好的答案


0

我不这么认为,至少不是以你定义的方式。在确定最大文件上传大小时,还有许多其他因素需要考虑,尤其是用户的连接速度以及Web服务器和PHP进程的超时设置。

对于你来说,更有用的指标可能是根据预期接收到的文件类型决定合理的最大文件大小。根据你的使用情况做出决策,并制定相应的政策。


当然,这就是我正在做的。但是我仍然非常需要知道php.ini中允许的大小是否低于我的推荐最大值。 - Zulakis

-1

你可以始终使用这个语法,它将从PHP ini文件中给出正确的数字:

$maxUpload      = (int)(ini_get('upload_max_filesize'));
$maxPost        = (int)(ini_get('post_max_size'));

马特


8
你有没有读过开头的帖子?echo ini_get('post_max_size');-> 8M echo(int)(ini_get('post_max_size'));-> 8 - Zulakis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接