网站后台有一个上传视频文件的选项。我上传一个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 大小的两倍才能正常执行
下面是底层实现,内容来源于网络
// 拷贝一份haystackhaystack_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);