FilesizeHelper.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <?php
  2. /**
  3. * FilesizeHelper.php UTF-8
  4. *
  5. * @date : 2016年9月12日下午8:15:33
  6. *
  7. * @license 这不是一个自由软件,未经授权不许任何使用和传播。
  8. * @author : wuyonghong <wyh@huosdk.com>
  9. * @version : H5 2.0
  10. */
  11. class FilesizeHelper {
  12. private $path; // the socket to the server
  13. /**
  14. * File size in bytes
  15. *
  16. * @var float
  17. * @access private
  18. */
  19. private $byteSize;
  20. /**
  21. * Whether we are on Windows platform or another OS (*nix and MacOS)
  22. *
  23. * @var bool
  24. * @access private
  25. */
  26. private $isWindows;
  27. /**
  28. * Constructor
  29. *
  30. * @access public
  31. *
  32. * @param void
  33. *
  34. * @return void
  35. */
  36. public function __construct() {
  37. // Check if we are on Windows platform
  38. $this->isWindows = (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
  39. }
  40. /**
  41. * Gets the size of the specified file
  42. *
  43. * @access public
  44. *
  45. * @param string The file path
  46. * @param bool Whether to format the file size in KB, MB, GB, TB
  47. *
  48. * @return mixed
  49. */
  50. public function getFileSize($file, $formatted = true) {
  51. // Set the path of the file
  52. $this->path = $file;
  53. if (strpos($this->path, "http") === 0) {
  54. $url = $this->path;
  55. $this->byteSize = $this->_url_exists($url);
  56. if (empty($this->byteSize)) {
  57. return 0;
  58. }
  59. } else {
  60. // Check for a valid file path
  61. $rs = $this->__checkFilePath();
  62. if (!$rs) {
  63. return 0;
  64. }
  65. // Get the file size in bytes
  66. $this->byteSize = (float)$this->__getByteSize();
  67. }
  68. // If failed to get the file size or the file size is zero, return a blank result
  69. if (!$this->byteSize) {
  70. if (!$formatted) {
  71. return 0;
  72. }
  73. // Return a blank array
  74. $blank_size = $this->__formatFileSize();
  75. return array(
  76. 0,
  77. $blank_size[0],
  78. $blank_size[1]
  79. );
  80. }
  81. // Return the bytesize if no formatting is needed
  82. if (!$formatted) {
  83. return $this->byteSize;
  84. }
  85. // Return an array containing the file size information
  86. return $this->__formatFileSize();
  87. }
  88. /**
  89. * Formats the file size in KB, MB, GB, TB units
  90. *
  91. * @access private
  92. *
  93. * @param void
  94. *
  95. * @return array Return arry containing the file size information
  96. */
  97. private function __formatFileSize() {
  98. // If the file size is zero return a blank result
  99. $_size = $this->byteSize;
  100. if (!$_size || $_size < 0) {
  101. return array(
  102. 0,
  103. '0 B',
  104. array(
  105. 0,
  106. 'B'
  107. )
  108. );
  109. }
  110. // If the file size is smaller than 1KB
  111. if ($_size <= 1024) {
  112. return array(
  113. 0,
  114. '1 KB',
  115. array(
  116. 1,
  117. 'KB'
  118. )
  119. );
  120. }
  121. // Set an array of all file size units
  122. $size_units = Array(
  123. 'B',
  124. 'KB',
  125. 'MB',
  126. 'GB',
  127. 'TB',
  128. 'PB',
  129. 'EB'
  130. );
  131. // Set the initial unit to Bytes
  132. $unit = $size_units[0];
  133. // Loop through all file size units
  134. for ($i = 1; ($i < count($size_units) && $_size >= 1024); $i++) {
  135. $_size = $_size / 1024;
  136. $unit = $size_units[$i];
  137. }
  138. // Set the number of digits after the decimal place in the resulted file size
  139. $round = 2;
  140. // If the file size is in KiloByte we do not need any decimal numbers
  141. if ($unit == 'KB') {
  142. $round = 0;
  143. }
  144. // Round the file size
  145. $formatted = round((float)$_size, $round);
  146. // Return the file size data
  147. return array(
  148. $this->byteSize,
  149. $formatted." ".$unit,
  150. array(
  151. $formatted,
  152. $unit
  153. )
  154. );
  155. }
  156. /**
  157. * Chek if the file is exist
  158. *
  159. * @access private
  160. *
  161. * @param void
  162. *
  163. * @return void
  164. */
  165. private function __checkFilePath() {
  166. clearstatcache();
  167. if (!file_exists($this->path)) {
  168. return false;
  169. }
  170. return true;
  171. }
  172. /**
  173. *
  174. * @link http://www.phpddt.com
  175. */
  176. private function _url_exists($url) {
  177. $ch = curl_init();
  178. curl_setopt($ch, CURLOPT_URL, $url);
  179. // 不下载
  180. curl_setopt($ch, CURLOPT_NOBODY, 1);
  181. // 设置超时
  182. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
  183. curl_setopt($ch, CURLOPT_TIMEOUT, 3);
  184. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  185. // curl_setopt($ch, CURLOPT_HEADER, true);
  186. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  187. curl_exec($ch);
  188. $http = curl_getinfo($ch);
  189. curl_close($ch);
  190. if ($http['http_code'] == 200) {
  191. return $http['download_content_length'];
  192. }
  193. return 0;
  194. }
  195. /**
  196. * Gets the size of the specified file in bytes
  197. *
  198. * @access private
  199. *
  200. * @param void
  201. *
  202. * @return string The file size in bytes
  203. */
  204. private function __getByteSize() {
  205. // Try the php native filesize() function.
  206. $bytesize = @filesize($this->path);
  207. if (false !== $bytesize && $bytesize >= 0) {
  208. return $bytesize;
  209. }
  210. // If filesize() fails with larger files, try to get the size using curl module.
  211. $bytesize = $this->__useCurl();
  212. if ($bytesize) {
  213. return $bytesize;
  214. }
  215. // If curl fails to get the file size try using the php native seek function.
  216. $bytesize = $this->__useNativeSeek();
  217. if ($bytesize) {
  218. return $bytesize;
  219. }
  220. // If the native seek fails to get the file size and we are on windows try using Windows COM interface
  221. $bytesize = $this->__useCom();
  222. if ($bytesize) {
  223. return $bytesize;
  224. }
  225. // If all the above methods failed to get the file size try using external program (exec() function needed)
  226. $bytesize = $this->__useExec();
  227. if ($bytesize) {
  228. return $bytesize;
  229. }
  230. // Unable to get the file size in bytes
  231. throw new Exception("Unable to get the file size for the file ".$this->path."!");
  232. }
  233. /**
  234. * Gets the file size using curl module
  235. *
  236. * @access private
  237. *
  238. * @param void
  239. *
  240. * @return mixed The file size as string or false when fail or cUrl module not available
  241. * @see http://www.php.net/manual/en/function.filesize.php#100434
  242. */
  243. private function __useCurl() {
  244. // If the curl module is not available return false
  245. if (!function_exists("curl_init")) {
  246. return false;
  247. }
  248. $ch = curl_init("file://".$this->path);
  249. curl_setopt($ch, CURLOPT_NOBODY, true);
  250. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  251. curl_setopt($ch, CURLOPT_HEADER, true);
  252. $data = curl_exec($ch);
  253. curl_close($ch);
  254. if ($data !== false && preg_match('/Content-Length: (\d+)/', $data, $matches)) {
  255. return (string)$matches[1];
  256. }
  257. }
  258. /**
  259. * Gets the file size by using native fseek function
  260. *
  261. * @access private
  262. *
  263. * @param void
  264. *
  265. * @return mixed The file size as string or false when fail
  266. * @see http://www.php.net/manual/en/function.filesize.php#79023
  267. * @see http://www.php.net/manual/en/function.filesize.php#102135
  268. */
  269. private function __useNativeSeek() {
  270. // This should work for large files on 64bit platforms and for small files every where
  271. $fp = @fopen($this->path, "rb");
  272. // If failed to open the file return false
  273. if (!$fp) {
  274. return false;
  275. }
  276. flock($fp, LOCK_SH);
  277. // Seeks past the end-of-file
  278. $res = fseek($fp, 0, SEEK_END);
  279. if ($res === 0) {
  280. // Get the current position of the file pointer
  281. $pos = ftell($fp);
  282. flock($fp, LOCK_UN);
  283. fclose($fp);
  284. // $pos will be positive int if file is <2GB
  285. // if is >2GB <4GB it will be negative number
  286. if ($pos >= 0) {
  287. return (string)$pos;
  288. } else {
  289. return sprintf("%u", $pos);
  290. }
  291. } else {
  292. flock($fp, LOCK_UN);
  293. fclose($fp);
  294. return false;
  295. }
  296. }
  297. /**
  298. * Gets the file size by using Windows COM interface
  299. *
  300. * @access private
  301. *
  302. * @param void
  303. *
  304. * @return mixed The file size as string or false when fail or COM not available
  305. */
  306. private function __useCom() {
  307. if (!$this->isWindows || !class_exists("COM")) {
  308. return false;
  309. }
  310. // Use the Windows COM interface
  311. $fsobj = new COM('Scripting.FileSystemObject');
  312. if (dirname($this->path) == '.') {
  313. $this->path = ((substr(getcwd(), -1) == DIRECTORY_SEPARATOR)
  314. ? getcwd().basename($this->path)
  315. : getcwd().DIRECTORY_SEPARATOR.basename(
  316. $this->path
  317. ));
  318. }
  319. // Get the file data
  320. $f = $fsobj->GetFile($this->path);
  321. // Convert to string
  322. return (string)$f->Size;
  323. }
  324. /**
  325. * Gets the file size by using external program (exec needed)
  326. *
  327. * @access private
  328. *
  329. * @param void
  330. *
  331. * @return mixed The file size as string or false when fail or or exec is disabled
  332. */
  333. private function __useExec() {
  334. // If exeec is disable return false
  335. if (!function_exists("exec")) {
  336. return false;
  337. }
  338. // Escape the file path string to be used as a shell argument
  339. $escapedPath = escapeshellarg($this->path);
  340. // If we are on Windows
  341. if ($this->isWindows) {
  342. // Try using the NT substition modifier %~z
  343. $size = trim(exec("for %F in ($escapedPath) do @echo %~zF"));
  344. } // If other OS (*nix and MacOS)
  345. else {
  346. // If the platform is not Windows, use the stat command (should work for *nix and MacOS)
  347. $size = trim(exec("stat -c%s $escapedPath"));
  348. }
  349. // If the return is not blank, not zero, and is number
  350. if ($size && ctype_digit($size)) {
  351. return (string)$size;
  352. }
  353. // An error has occured
  354. return false;
  355. }
  356. }