网站后台有一个上传视频文件的选项。我上传一个25M的视频,结果报Fatal error
php.ini中的配置:
file_uploads = On
max_execution_time =90
max_input_time = 90
memory_limit = 50M
post_max_size =40M
upload_max_filesize =40M
default_socket_timeout = 80
关键代码:
public function checkContent($uploadInfo) {
$file_content = $this->readover(BASE_ROOT.$uploadInfo['source']);
if (empty($file_content)) {
@unlink(BASE_ROOT.$uploadInfo['source']);
return 'upload_content_error';
}
else {
$forbid_chars = array(
'0'=>"?php",
'1'=>"cmd.exe",
'2'=>"mysql_connect",
'3'=>"phpinfo()",
'4'=>"get_current_user",
'5'=>"zend",
'6'=>"_GET",
'7'=>"_POST",
'8'=>"_REQUEST",
'9'=>"base64_decode",
'10'=>"echo",
'11'=>"?PHP",
);
foreach ($forbid_chars as $key=>$value) {
printf('memory usage: %01.2f MB | ', memory_get_usage()/1024/1024);
if (stripos($file_content, $value)) {//程序走到这个位置报错!
@unlink(BASE_ROOT.$uploadInfo['source']);
return 'upload_content_error';
break;
}
}
}
if (in_array(strtolower($uploadInfo['ext']), array('gif','jpg','jpeg','png'))) {
if (!$this->getFileSize($uploadInfo['source'])) {
@unlink(BASE_ROOT.$uploadInfo['source']);
return 'input_max_file_size';
}
}
return true;
}
private function readover($fileName, $method = 'rb') {
$data = '';
if ($handle = @fopen($fileName, $method)) {
flock($handle, LOCK_SH);
$data = @fread($handle, filesize($fileName));
fclose($handle);
}
return $data;
}
后来设置memory_limit = 55M,接近当前占用内存的两倍时,就可以上传了。
stripos($file_content, $value) 执行过程中会再生成一份 $file_content 变量放在内存中
假设你 $file_content 大小为 26M
在程序运行到 stripos()函数时,内存会瞬间增长到 52M,但是 memory_limit = 50M
这时候会出现内存超出问题
所以设置 memory_limit = 55M 超过 26M 大小的两倍才能正常执行
下面是底层实现,内容来源于网络
// 拷贝一份haystack
haystack_dup = estrndup(haystack, haystack_len);
if (Z_TYPE_P(needle) == IS_STRING) {
char *orig_needle;
if (!Z_STRLEN_P(needle)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle");
efree(haystack_dup);
RETURN_FALSE;
}
orig_needle = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
// 调用php_stristr函数找出orig_needle的值。
found = php_stristr(haystack_dup, orig_needle, haystack_len, Z_STRLEN_P(needle));
efree(orig_needle);
} else {
if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
efree(haystack_dup);
RETURN_FALSE;
}
needle_char[1] = 0;
found = php_stristr(haystack_dup, needle_char, haystack_len, 1);
}
if (found) {
found_offset = found - haystack_dup;
if (part) {
RETVAL_STRINGL(haystack, found_offset, 1);
} else {
RETVAL_STRINGL(haystack + found_offset, haystack_len - found_offset, 1);
}
} else {
RETVAL_FALSE;
}
// 释放变量
efree(haystack_dup);