upload.init.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <?php
  2. if (!defined('IS_INITPHP')) exit('Access Denied!');
  3. /*********************************************************************************
  4. * InitPHP 3.8.2 国产PHP开发框架 扩展类库-文件上传
  5. *-------------------------------------------------------------------------------
  6. * 版权所有: CopyRight By initphp.com
  7. * 您可以自由使用该源码,但是在使用过程中,请保留作者信息。尊重他人劳动成果就是尊重自己
  8. *-------------------------------------------------------------------------------
  9. * Author:zhuli Dtime:2014-11-25
  10. ***********************************************************************************/
  11. class uploadInit {
  12. const UPLOAD_ERR_INI_SIZE = 1;
  13. const INPUT_MAX_FILE_SIZE = 2;
  14. const UPLOAD_HALF = 3;
  15. const UPLOAD_ERR_NO_TMP_DIR = 4;
  16. private $params; //参数
  17. private $defaultMaxSize = 2048; //上传文件默认最大值
  18. private $defaultAllowFileType = array('gif','jpeg','jpg','png','bmp','swf', 'txt');
  19. private $errorCodeArr = array(
  20. 'upload_error' => -1, //上传失败
  21. 'not_upload_files' => -2, //不是通过HTTP POST方法上传
  22. 'not_an_allowed_type' => -3, //不允许的上传类型
  23. 'file_size_is_large' => -4, //文件太大
  24. 'upload_err_ini_size' => -5, //上传文件超过服务器上传限制
  25. 'input_max_file_size' => -6, //上传文件超过表达最大上传限制
  26. 'upload_half' => -7, //只上传了一半文件
  27. 'upload_err_no_tmp_dir'=> -8, //上传的临时目录出错
  28. 'illegal_file_type' => -9, //新的文件名,命名不合法
  29. 'upload_content_error' => -10 //上传的内容不合法
  30. ); //错误码
  31. /**
  32. * 上传文件 主函数
  33. * @param $name 上传文件名
  34. * @param $newName 新的文件名 不需要类型
  35. * @param $path 目录
  36. * @param $params 参数配置
  37. * @return array
  38. */
  39. public function upload($name, $newName, $path, $params = array()) {
  40. $this->params = $this->parseParams($params);
  41. $uploadInfo = $this->init($name, $newName, $path);
  42. if (!$uploadInfo) return $this->error('upload_error'); //是否正常上传
  43. $errorVal = $this->checkUpload($uploadInfo['error']);
  44. if ($errorVal !== true) return $this->error($errorVal); //检测上传错误码
  45. if (!$this->checkIsUploadFile($uploadInfo['tmp_name'])) return $this->error('not_upload_files'); //是否通过HTTP POST上传
  46. if (!$this->checkType($uploadInfo['ext'])) return $this->error('not_an_allowed_type'); //是否允许上传的类型
  47. if (!$this->checkSize($uploadInfo['size'])) return $this->error('file_size_is_large'); //文件大小
  48. if (!$this->checkNewName($newName)) return $this->error('illegal_file_type'); //新文件名是否合法
  49. $result = $this->save($uploadInfo['tmp_name'], $uploadInfo['source'], $uploadInfo['path']);
  50. if ($result == false) {
  51. return $this->error('upload_error');
  52. } else {
  53. $checkContentResult = $this->checkContent($uploadInfo);
  54. if ($checkContentResult !== true) return $this->error($checkContentResult); //检测上传错误码
  55. return $uploadInfo;
  56. }
  57. }
  58. /**
  59. * 参数设置
  60. * @param array $params array('maxSize' => 文件上传最大,'allowFileType' => 允许上传的文件类型)
  61. * @return
  62. */
  63. public function setParams($params) {
  64. $this->params = $this->parseParams($params);
  65. }
  66. /**
  67. * 装载上传文件的信息
  68. * @param string $name 上传文件名
  69. * @return array
  70. */
  71. private function init($name, $newName, $path) {
  72. $newName = $this->escapeStr($newName);
  73. $path = $this->escapeDir($path);
  74. $file = $_FILES[$name];
  75. if (!$file['tmp_name'] || $file['tmp_name'] == '') return false;
  76. $file['name'] = $this->escapeStr($file['name']);
  77. $file['ext'] = strtolower(substr(strrchr($file['name'], '.'), 1));
  78. $file['size'] = intval($file['size']);
  79. $file['type'] = $file['type'];
  80. $file['tmp_name'] = $file['tmp_name'];
  81. $file['source'] = $path .'/'. $newName . '.' . $file['ext']; //路径
  82. $file['path'] = $path; //目录
  83. $file['newName'] = $newName. '.' . $file['ext']; //文件名
  84. return $file;
  85. }
  86. /**
  87. * 参数处理
  88. * @param array $params 文件上传配置参数
  89. * @return
  90. */
  91. private function parseParams(array $params) {
  92. $temp = array();
  93. $temp['maxSize'] = (isset($params['maxSize'])) ? (int)$params['maxSize'] : $this->defaultMaxSize;
  94. $temp['allowFileType'] = (is_array($params['allowFileType'])) ? $params['allowFileType'] : $this->defaultAllowFileType;
  95. return $temp;
  96. }
  97. /**
  98. * 保存文件
  99. * @param $name 上传文件名
  100. * @param $newName 新的文件名 1
  101. * @param $path 目录
  102. * @return bool
  103. */
  104. private function save($tmpName, $filename, $path) {
  105. $this->createFolder($path); //创建目录
  106. if (function_exists("move_uploaded_file") && @move_uploaded_file($tmpName, $filename)) {
  107. @chmod($filename, 0777);
  108. return true;
  109. } elseif (@copy($tmpName, $filename)) {
  110. @chmod($filename, 0777);
  111. return true;
  112. }
  113. return false;
  114. }
  115. /**
  116. * 错误码检测
  117. * @param int $error 错误状态
  118. * @return string
  119. */
  120. private function checkUpload($error) {
  121. if ($error == uploadInit::UPLOAD_ERR_INI_SIZE) { //上传是否超过ini设置
  122. return 'upload_err_ini_size';
  123. } elseif ($error == uploadInit::INPUT_MAX_FILE_SIZE) { //上传是否超过表单设置
  124. return 'input_max_file_size';
  125. } elseif ($error == uploadInit::UPLOAD_HALF) { //上传一半
  126. return 'upload_half';
  127. } elseif ($error == uploadInit::UPLOAD_ERR_NO_TMP_DIR) { //上传临时目录创建错误
  128. return 'upload_err_no_tmp_dir';
  129. } else {
  130. return true;
  131. }
  132. }
  133. /**
  134. * 文件类型检测
  135. * @param string $uploadType 类型
  136. * @return bool
  137. */
  138. private function checkType($uploadType) {
  139. return (empty($uploadType) || !in_array($uploadType, $this->params['allowFileType'])) ? false : true;
  140. }
  141. /**
  142. * 文件大小检测
  143. * @param int $uploadSize 大小
  144. * @return bool
  145. */
  146. private function checkSize($uploadSize) {
  147. return ($uploadSize < 1 || $uploadSize > ($this->params['maxSize'] * 1024)) ? false : true;
  148. }
  149. /**
  150. * 检测新的文件名
  151. * @param string $newName 新文件名
  152. * @return bool
  153. */
  154. private function checkNewName($newName) {
  155. $newName = strtolower($newName);
  156. return (strpos($newName, '..') !== false || strpos($newName, '.php.') !== false || eregi("\.php$", $newName)) ? false : true;
  157. }
  158. /**
  159. * 检测是否是上传的文件
  160. * @param $tmpName 临时文件名
  161. * @return bool
  162. */
  163. private function checkIsUploadFile($tmpName) {
  164. if (!$tmpName || $tmpName == 'none') {
  165. return false;
  166. } elseif (function_exists('is_uploaded_file') && !is_uploaded_file($tmpName) && !is_uploaded_file(str_replace('\\\\', '\\', $tmpName))) {
  167. return false;
  168. } else {
  169. return true;
  170. }
  171. }
  172. /**
  173. * 文件上传后检测文件内容是否合法
  174. * @param string $uploadInfo 文件信息
  175. * @param string $source 文件源目录
  176. * @return bool
  177. */
  178. private function checkContent($uploadInfo) {
  179. if ($uploadInfo['ext'] == 'txt') {
  180. if (preg_match('/(onload|submit|post|form)/i', $this->readover($uploadInfo['source']))) {
  181. @unlink($uploadInfo['source']);
  182. return 'upload_content_error';
  183. }
  184. } elseif (in_array($uploadInfo['ext'], array('gif','jpg','jpeg','png','bmp','swf'))) {
  185. if (!$img_size = $this->getImgSize($uploadInfo['source'], $uploadInfo['ext'])) {
  186. @unlink($uploadInfo['source']);
  187. return 'upload_content_error';
  188. }
  189. }
  190. return true;
  191. }
  192. /**
  193. * 创建目录 如果目录存在,则不创建,不存在则创建 static
  194. * @param $path 路径
  195. * @return
  196. */
  197. public static function createFolder($path) {
  198. if (!is_dir($path)) {
  199. mkdir($path, 0755, true);
  200. }
  201. //下面这段容易造成递归死循环
  202. // if (!is_dir($path)) {
  203. // uploadInit::createFolder(dirname($path));
  204. // @mkdir($path);
  205. // @chmod($path, 0777);
  206. // @fclose(@fopen($path . '/index.html', 'w'));
  207. // @chmod($path . '/index.html', 0777);
  208. // }
  209. }
  210. /**
  211. * 读取文件
  212. * @param string $fileName 文件绝对路径
  213. * @param string $method 读取模式
  214. */
  215. private function readover($fileName, $method = 'rb') {
  216. $fileName = $this->escapePath($fileName);
  217. $data = '';
  218. if ($handle = @fopen($fileName, $method)) {
  219. flock($handle, LOCK_SH);
  220. $data = @fread($handle, filesize($fileName));
  221. fclose($handle);
  222. }
  223. return $data;
  224. }
  225. /**
  226. * 获取图片的大小
  227. * @param string $srcFile 图片地址
  228. * @param string $srcExt 图片类型
  229. * @return
  230. */
  231. private function getImgSize($srcFile, $srcExt = null) {
  232. empty($srcExt) && $srcExt = strtolower(substr(strrchr($srcFile, '.'), 1));
  233. $srcdata = array();
  234. if (function_exists('read_exif_data') && in_array($srcExt, array(
  235. 'jpg',
  236. 'jpeg',
  237. 'jpe',
  238. 'jfif'
  239. ))) {
  240. $datatemp = @read_exif_data($srcFile);
  241. $srcdata['width'] = $datatemp['COMPUTED']['Width'];
  242. $srcdata['height'] = $datatemp['COMPUTED']['Height'];
  243. $srcdata['type'] = 2;
  244. unset($datatemp);
  245. }
  246. !$srcdata['width'] && list($srcdata['width'], $srcdata['height'], $srcdata['type']) = @getimagesize($srcFile);
  247. if (!$srcdata['type'] || ($srcdata['type'] == 1 && in_array($srcExt, array(
  248. 'jpg',
  249. 'jpeg',
  250. 'jpe',
  251. 'jfif'
  252. )))) { //noizy fix
  253. return false;
  254. }
  255. return $srcdata;
  256. }
  257. /**
  258. * 字符转换
  259. * @param string $string 转换的字符串
  260. * @return string 返回转换后的字符串
  261. */
  262. private function escapeStr($string) {
  263. $string = str_replace(array("\0","%00","\r"), '', $string);
  264. $string = preg_replace(array('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/','/&(?!(#[0-9]+|[a-z]+);)/is'), array('', '&amp;'), $string);
  265. $string = str_replace(array("%3C",'<'), '&lt;', $string);
  266. $string = str_replace(array("%3E",'>'), '&gt;', $string);
  267. $string = str_replace(array('"',"'","\t",' '), array('&quot;','&#39;',' ','&nbsp;&nbsp;'), $string);
  268. return $string;
  269. }
  270. /**
  271. * 目录转换
  272. * @param string $dir
  273. * @return string
  274. */
  275. private function escapeDir($dir) {
  276. $dir = str_replace(array("'",'#','=','`','$','%','&',';'), '', $dir);
  277. return rtrim(preg_replace('/(\/){2,}|(\\\){1,}/', '/', $dir), '/');
  278. }
  279. /**
  280. * 私用路径转换
  281. * @param $fileName
  282. * @param $ifCheck
  283. * @return boolean
  284. */
  285. private function escapePath($fileName, $ifCheck = true) {
  286. $tmpname = strtolower($fileName);
  287. $tmparray = array('://',"\0");
  288. $ifCheck && $tmparray[] = '..';
  289. if (str_replace($tmparray, '', $tmpname) != $tmpname) {
  290. return false;
  291. }
  292. return true;
  293. }
  294. /**
  295. * 上传错误提示
  296. * @param unknown_type $msgType
  297. */
  298. private function error($errorCode) {
  299. return $this->errorCodeArr[$errorCode];
  300. }
  301. }