upload.init.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <?php
  2. if (! defined ( 'IS_INITPHP' ))
  3. exit ( 'Access Denied!' );
  4. /**
  5. * *******************************************************************************
  6. * InitPHP 3.3 国产PHP开发框架 扩展类库-文件上传
  7. * -------------------------------------------------------------------------------
  8. * 版权所有: CopyRight By initphp.com
  9. * 您可以自由使用该源码,但是在使用过程中,请保留作者信息。尊重他人劳动成果就是尊重自己
  10. * -------------------------------------------------------------------------------
  11. * $Author:zhuli
  12. * $Dtime:2013-5-29
  13. * *********************************************************************************
  14. */
  15. class uploadInit {
  16. const UPLOAD_ERR_INI_SIZE = 1;
  17. const INPUT_MAX_FILE_SIZE = 2;
  18. const UPLOAD_HALF = 3;
  19. const UPLOAD_ERR_NO_TMP_DIR = 4;
  20. private $params; // 参数
  21. private $defaultMaxSize = 2048; // 上传文件默认最大值
  22. private $defaultAllowFileType = array (
  23. 'gif',
  24. 'jpeg',
  25. 'jpg',
  26. 'png',
  27. 'bmp',
  28. 'swf',
  29. 'txt'
  30. );
  31. private $errorCodeArr = array (
  32. 'upload_error' => - 1, // 上传失败
  33. 'not_upload_files' => - 2, // 不是通过HTTP POST方法上传
  34. 'not_an_allowed_type' => - 3, // 不允许的上传类型
  35. 'file_size_is_large' => - 4, // 文件太大
  36. 'upload_err_ini_size' => - 5, // 上传文件超过服务器上传限制
  37. 'input_max_file_size' => - 6, // 上传文件超过表达最大上传限制
  38. 'upload_half' => - 7, // 只上传了一半文件
  39. 'upload_err_no_tmp_dir' => - 8, // 上传的临时目录出错
  40. 'illegal_file_type' => - 9, // 新的文件名,命名不合法
  41. 'upload_content_error' => - 10 // 上传的内容不合法
  42. ); // 错误码
  43. /**
  44. * 上传文件 主函数
  45. *
  46. * @param $name 上传文件名
  47. * @param $newName 新的文件名
  48. * 不需要类型
  49. * @param $path 目录
  50. * @param $params 参数配置
  51. * @return array
  52. */
  53. public function upload($name, $newName, $path, $params = array()) {
  54. $this->params = $this->parseParams ( $params );
  55. $uploadInfo = $this->init ( $name, $newName, $path );
  56. if (! $uploadInfo)
  57. return $this->error ( 'upload_error' ); // 是否正常上传
  58. $errorVal = $this->checkUpload ( $uploadInfo ['error'] );
  59. if ($errorVal !== true)
  60. return $this->error ( $errorVal ); // 检测上传错误码
  61. if (! $this->checkIsUploadFile ( $uploadInfo ['tmp_name'] ))
  62. return $this->error ( 'not_upload_files' ); // 是否通过HTTP POST上传
  63. $filetype=$this->checkFileType($uploadInfo['tmp_name']);
  64. if (! $this->checkType ( $filetype))
  65. return $this->error ( 'not_an_allowed_type' ); // 是否允许上传的类型
  66. $filetype=$uploadInfo['ext'];
  67. if (! $this->checkType ( $filetype))
  68. return $this->error ( 'not_an_allowed_type' ); // 是否允许上传的类型
  69. if (! $this->checkSize ( $uploadInfo ['size'] ))
  70. return $this->error ( 'file_size_is_large' ); // 文件大小
  71. if (! $this->checkNewName ( $newName ))
  72. return $this->error ( 'illegal_file_type' ); // 新文件名是否合法
  73. $result = $this->save ( $uploadInfo ['tmp_name'], $uploadInfo ['source'], $uploadInfo ['path'] );
  74. if ($result == false) {
  75. return $this->error ( 'upload_error' );
  76. } else {
  77. $checkContentResult = $this->checkContent ( $uploadInfo );
  78. if ($checkContentResult !== true)
  79. return $this->error ( $checkContentResult ); // 检测上传错误码
  80. return $uploadInfo;
  81. }
  82. }
  83. /**
  84. * 读取文件前几个字节 判断文件类型
  85. *
  86. * @return string
  87. */
  88. function checkFileType($filename) {
  89. $file = fopen ( $filename, 'rb' );
  90. $bin = fread ( $file, 2 ); // 只读2字节
  91. fclose ( $file );
  92. $strInfo = @unpack ( "c2chars", $bin );
  93. $typeCode = intval ( $strInfo ['chars1'] . $strInfo ['chars2'] );
  94. $fileType = '';
  95. switch ($typeCode) {
  96. case 7790 :
  97. $fileType = 'exe';
  98. break;
  99. case 7784 :
  100. $fileType = 'midi';
  101. break;
  102. case 8297 :
  103. $fileType = 'rar';
  104. break;
  105. case 255216 :
  106. $fileType = 'jpg';
  107. break;
  108. case 8075:
  109. $fileType = 'zip';
  110. break;
  111. case 7173 :
  112. $fileType = 'gif';
  113. break;
  114. case 6677 :
  115. $fileType = 'bmp';
  116. break;
  117. case 13780 :
  118. $fileType = 'png';
  119. break;
  120. default :
  121. $fileType = 'unknown' . $typeCode;
  122. break;
  123. }
  124. // Fix
  125. if ($strInfo ['chars1'] == '-1' && $strInfo ['chars2'] == '-40') {
  126. return 'jpg';
  127. }
  128. if ($strInfo ['chars1'] == '-119' && $strInfo ['chars2'] == '80') {
  129. return 'png';
  130. }
  131. return $fileType;
  132. }
  133. /**
  134. * 参数设置
  135. *
  136. * @param array $params
  137. * array('maxSize' => 文件上传最大,'allowFileType' => 允许上传的文件类型)
  138. * @return
  139. *
  140. */
  141. public function setParams($params) {
  142. $this->params = $this->parseParams ( $params );
  143. }
  144. /**
  145. * 装载上传文件的信息
  146. *
  147. * @param string $name
  148. * 上传文件名
  149. * @return array
  150. */
  151. private function init($name, $newName, $path) {
  152. $newName = $this->escapeStr ( $newName );
  153. $path = $this->escapeDir ( $path );
  154. $file = $_FILES [$name];
  155. if (! $file ['tmp_name'] || $file ['tmp_name'] == '')
  156. return false;
  157. $file ['name'] = $this->escapeStr ( $file ['name'] );
  158. $file ['ext'] = strtolower ( substr ( strrchr ( $file ['name'], '.' ), 1 ) );
  159. $file ['size'] = intval ( $file ['size'] );
  160. $file ['type'] = $file ['type'];
  161. $file ['tmp_name'] = $file ['tmp_name'];
  162. $file ['source'] = $path . '/' . $newName . '.' . $file ['ext']; // 路径
  163. $file ['path'] = $path; // 目录
  164. $file ['newName'] = $newName . '.' . $file ['ext']; // 文件名
  165. return $file;
  166. }
  167. /**
  168. * 参数处理
  169. *
  170. * @param array $params
  171. * 文件上传配置参数
  172. * @return
  173. *
  174. */
  175. private function parseParams(array $params) {
  176. $temp = array ();
  177. $temp ['maxSize'] = (isset ( $params ['maxSize'] )) ? ( int ) $params ['maxSize'] : $this->defaultMaxSize;
  178. $temp ['allowFileType'] = (is_array ( $params ['allowFileType'] )) ? $params ['allowFileType'] : $this->defaultAllowFileType;
  179. return $temp;
  180. }
  181. /**
  182. * 保存文件
  183. *
  184. * @param $name 上传文件名
  185. * @param $newName 新的文件名
  186. * 1
  187. * @param $path 目录
  188. * @return bool
  189. */
  190. private function save($tmpName, $filename, $path) {
  191. $this->createFolder ( $path ); // 创建目录
  192. if (function_exists ( "move_uploaded_file" ) && @move_uploaded_file ( $tmpName, $filename )) {
  193. @chmod ( $filename, 0777 );
  194. return true;
  195. } elseif (@copy ( $tmpName, $filename )) {
  196. @chmod ( $filename, 0777 );
  197. return true;
  198. }
  199. return false;
  200. }
  201. /**
  202. * 错误码检测
  203. *
  204. * @param int $error
  205. * 错误状态
  206. * @return string
  207. */
  208. private function checkUpload($error) {
  209. if ($error == uploadInit::UPLOAD_ERR_INI_SIZE) { // 上传是否超过ini设置
  210. return 'upload_err_ini_size';
  211. } elseif ($error == uploadInit::INPUT_MAX_FILE_SIZE) { // 上传是否超过表单设置
  212. return 'input_max_file_size';
  213. } elseif ($error == uploadInit::UPLOAD_HALF) { // 上传一半
  214. return 'upload_half';
  215. } elseif ($error == uploadInit::UPLOAD_ERR_NO_TMP_DIR) { // 上传临时目录创建错误
  216. return 'upload_err_no_tmp_dir';
  217. } else {
  218. return true;
  219. }
  220. }
  221. /**
  222. * 文件类型检测
  223. *
  224. * @param string $uploadType
  225. * 类型
  226. * @return bool
  227. */
  228. private function checkType($uploadType) {
  229. return (empty ( $uploadType ) || ! in_array ( $uploadType, $this->params ['allowFileType'] )) ? false : true;
  230. }
  231. /**
  232. * 文件大小检测
  233. *
  234. * @param int $uploadSize
  235. * 大小
  236. * @return bool
  237. */
  238. private function checkSize($uploadSize) {
  239. return ($uploadSize < 1 || $uploadSize > ($this->params ['maxSize'] * 1024)) ? false : true;
  240. }
  241. /**
  242. * 检测新的文件名
  243. *
  244. * @param string $newName
  245. * 新文件名
  246. * @return bool
  247. */
  248. private function checkNewName($newName) {
  249. $newName = strtolower ( $newName );
  250. return (strpos ( $newName, '..' ) !== false || strpos ( $newName, '.php.' ) !== false || eregi ( "\.php$", $newName )) ? false : true;
  251. }
  252. /**
  253. * 检测是否是上传的文件
  254. *
  255. * @param $tmpName 临时文件名
  256. * @return bool
  257. */
  258. private function checkIsUploadFile($tmpName) {
  259. if (! $tmpName || $tmpName == 'none') {
  260. return false;
  261. } elseif (function_exists ( 'is_uploaded_file' ) && ! is_uploaded_file ( $tmpName ) && ! is_uploaded_file ( str_replace ( '\\\\', '\\', $tmpName ) )) {
  262. return false;
  263. } else {
  264. return true;
  265. }
  266. }
  267. /**
  268. * 文件上传后检测文件内容是否合法
  269. *
  270. * @param string $uploadInfo
  271. * 文件信息
  272. * @param string $source
  273. * 文件源目录
  274. * @return bool
  275. */
  276. private function checkContent($uploadInfo) {
  277. if ($uploadInfo ['ext'] == 'txt') {
  278. if (preg_match ( '/(onload|submit|post|form)/i', $this->readover ( $uploadInfo ['source'] ) )) {
  279. @unlink ( $uploadInfo ['source'] );
  280. return 'upload_content_error';
  281. }
  282. } elseif (in_array ( $uploadInfo ['ext'], array (
  283. 'gif',
  284. 'jpg',
  285. 'jpeg',
  286. 'png',
  287. 'bmp',
  288. 'swf'
  289. ) )) {
  290. if (! $img_size = $this->getImgSize ( $uploadInfo ['source'], $uploadInfo ['ext'] )) {
  291. @unlink ( $uploadInfo ['source'] );
  292. return 'upload_content_error';
  293. }
  294. }
  295. return true;
  296. }
  297. /**
  298. * 创建目录 如果目录存在,则不创建,不存在则创建 static
  299. *
  300. * @param $path 路径
  301. * @return
  302. *
  303. */
  304. public static function createFolder($path) {
  305. if (! is_dir ( $path )) {
  306. uploadInit::createFolder ( dirname ( $path ) );
  307. @mkdir ( $path );
  308. @chmod ( $path, 0777 );
  309. @fclose ( @fopen ( $path . '/index.html', 'w' ) );
  310. @chmod ( $path . '/index.html', 0777 );
  311. }
  312. }
  313. /**
  314. * 读取文件
  315. *
  316. * @param string $fileName
  317. * 文件绝对路径
  318. * @param string $method
  319. * 读取模式
  320. */
  321. private function readover($fileName, $method = 'rb') {
  322. $fileName = $this->escapePath ( $fileName );
  323. $data = '';
  324. if ($handle = @fopen ( $fileName, $method )) {
  325. flock ( $handle, LOCK_SH );
  326. $data = @fread ( $handle, filesize ( $fileName ) );
  327. fclose ( $handle );
  328. }
  329. return $data;
  330. }
  331. /**
  332. * 获取图片的大小
  333. *
  334. * @param string $srcFile
  335. * 图片地址
  336. * @param string $srcExt
  337. * 图片类型
  338. * @return
  339. *
  340. */
  341. private function getImgSize($srcFile, $srcExt = null) {
  342. empty ( $srcExt ) && $srcExt = strtolower ( substr ( strrchr ( $srcFile, '.' ), 1 ) );
  343. $srcdata = array ();
  344. if (function_exists ( 'read_exif_data' ) && in_array ( $srcExt, array (
  345. 'jpg',
  346. 'jpeg',
  347. 'jpe',
  348. 'jfif'
  349. ) )) {
  350. $datatemp = @read_exif_data ( $srcFile );
  351. $srcdata ['width'] = $datatemp ['COMPUTED'] ['Width'];
  352. $srcdata ['height'] = $datatemp ['COMPUTED'] ['Height'];
  353. $srcdata ['type'] = 2;
  354. unset ( $datatemp );
  355. }
  356. ! $srcdata ['width'] && list ( $srcdata ['width'], $srcdata ['height'], $srcdata ['type'] ) = @getimagesize ( $srcFile );
  357. if (! $srcdata ['type'] || ($srcdata ['type'] == 1 && in_array ( $srcExt, array (
  358. 'jpg',
  359. 'jpeg',
  360. 'jpe',
  361. 'jfif'
  362. ) ))) { // noizy fix
  363. return false;
  364. }
  365. return $srcdata;
  366. }
  367. /**
  368. * 字符转换
  369. *
  370. * @param string $string
  371. * 转换的字符串
  372. * @return string 返回转换后的字符串
  373. */
  374. private function escapeStr($string) {
  375. $string = str_replace ( array (
  376. "\0",
  377. "%00",
  378. "\r"
  379. ), '', $string );
  380. $string = preg_replace ( array (
  381. '/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/',
  382. '/&(?!(#[0-9]+|[a-z]+);)/is'
  383. ), array (
  384. '',
  385. '&amp;'
  386. ), $string );
  387. $string = str_replace ( array (
  388. "%3C",
  389. '<'
  390. ), '&lt;', $string );
  391. $string = str_replace ( array (
  392. "%3E",
  393. '>'
  394. ), '&gt;', $string );
  395. $string = str_replace ( array (
  396. '"',
  397. "'",
  398. "\t",
  399. ' '
  400. ), array (
  401. '&quot;',
  402. '&#39;',
  403. ' ',
  404. '&nbsp;&nbsp;'
  405. ), $string );
  406. return $string;
  407. }
  408. /**
  409. * 目录转换
  410. *
  411. * @param string $dir
  412. * @return string
  413. */
  414. private function escapeDir($dir) {
  415. $dir = str_replace ( array (
  416. "'",
  417. '#',
  418. '=',
  419. '`',
  420. '$',
  421. '%',
  422. '&',
  423. ';'
  424. ), '', $dir );
  425. return rtrim ( preg_replace ( '/(\/){2,}|(\\\){1,}/', '/', $dir ), '/' );
  426. }
  427. /**
  428. * 私用路径转换
  429. *
  430. * @param
  431. * $fileName
  432. * @param
  433. * $ifCheck
  434. * @return boolean
  435. */
  436. private function escapePath($fileName, $ifCheck = true) {
  437. $tmpname = strtolower ( $fileName );
  438. $tmparray = array (
  439. '://',
  440. "\0"
  441. );
  442. $ifCheck && $tmparray [] = '..';
  443. if (str_replace ( $tmparray, '', $tmpname ) != $tmpname) {
  444. return false;
  445. }
  446. return true;
  447. }
  448. /**
  449. * 上传错误提示
  450. *
  451. * @param unknown_type $msgType
  452. */
  453. private function error($errorCode) {
  454. return $this->errorCodeArr [$errorCode];
  455. }
  456. }