Download.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. namespace loyalsoft\Util;
  3. include_once __DIR__ . '/BreakContinue.php';
  4. include_once __DIR__ . '/HttpRequest.php';
  5. include_once __DIR__ . '/../traits/ClassEvent.php';
  6. class Download
  7. {
  8. /**
  9. * 使用Event接口
  10. */
  11. use ClassEvent;
  12. /**
  13. * HttpRequest
  14. * @var HttpRequest
  15. */
  16. public $http;
  17. /**
  18. * 下载地址
  19. * @var string
  20. */
  21. public $url;
  22. /**
  23. * 参数
  24. * @var string
  25. */
  26. public $params;
  27. /**
  28. * 请求方法
  29. * @var string
  30. */
  31. public $method;
  32. /**
  33. * HttpResponse
  34. *
  35. * @var HttpResponse
  36. */
  37. public $response;
  38. /**
  39. * 断点续传是否开启,默认为自动检测。常量BreakContinue::
  40. * @var int
  41. */
  42. public $breakContinue = BreakContinue::AUTO;
  43. /**
  44. * 文件大小
  45. * @var int
  46. */
  47. public $fileSize;
  48. /**
  49. * 是否开启断点续传
  50. * @var bool
  51. */
  52. public $isBreakContinue;
  53. /**
  54. * 每个分块大小,单位:字节,默认为1M
  55. * @var int
  56. */
  57. public $blockSize = 1048576;
  58. public function __construct($url, $params = array(), $method = 'GET')
  59. {
  60. $this->url = $url;
  61. $this->params = $params;
  62. $this->method = $method;
  63. $this->http = new HttpRequest();
  64. }
  65. /**
  66. * 获取文件大小,单位:字节。
  67. * @return mixed
  68. */
  69. public function getFileSize()
  70. {
  71. $this->response = $this->http->headers(array(
  72. 'Range' => 'bytes=0-1',
  73. ))->options(array(
  74. CURLOPT_NOBODY => true
  75. ))->send($this->url, $this->params, $this->method);
  76. if (isset($this->response->headers['Content-Range'])) {
  77. list(, $length) = explode('/', $this->response->headers['Content-Range']);
  78. return (int) $length;
  79. } else {
  80. return false;
  81. }
  82. }
  83. public function download($filename)
  84. {
  85. $this->fileSize = $this->getFileSize();
  86. if ($this->fileSize) {
  87. $canBreakContinue = isset($this->response->headers['Content-Range']);
  88. switch ($this->breakContinue) {
  89. case BreakContinue::AUTO:
  90. $this->isBreakContinue = $canBreakContinue;
  91. break;
  92. case BreakContinue::ON:
  93. $this->isBreakContinue = (true === $canBreakContinue);
  94. break;
  95. case BreakContinue::OFF:
  96. $this->isBreakContinue = false;
  97. break;
  98. }
  99. } else {
  100. $canBreakContinue = $this->isBreakContinue = false;
  101. }
  102. $this->http->options(array(
  103. CURLOPT_NOBODY => false
  104. ));
  105. if ($this->isBreakContinue) {
  106. $fp = fopen($filename, 'a+');
  107. if (false === $fp) {
  108. throw new \Exception('打开本地文件失败');
  109. }
  110. $begin = filesize($filename);
  111. if (false === $begin) {
  112. throw new \Exception('获取本地文件大小失败');
  113. }
  114. while ($begin < $this->fileSize) {
  115. $length = min($this->fileSize - $begin, $this->blockSize);
  116. $this->response = $this->http->headers(array(
  117. 'Range' => 'bytes=' . $begin . '-' . ($begin + $length),
  118. ))->send($this->url, $this->params, $this->method);
  119. if (false === fwrite($fp, $this->response->body)) {
  120. fclose($fp);
  121. throw new \Exception('文件写入失败');
  122. }
  123. $begin += $this->response->headers['Content-Length'];
  124. $this->trigger('progressChanged', array(
  125. 'length' => $this->fileSize,
  126. 'completeLength' => $begin,
  127. 'percent' => $begin / $this->fileSize,
  128. ));
  129. }
  130. fclose($fp);
  131. } else {
  132. $this->http->download($filename, $this->url, $this->params, $this->method);
  133. }
  134. }
  135. }
  136. try {
  137. $download = new Download('http://dldir1.qq.com/weixin/Windows/WeChatSetup.exe');
  138. $download->blockSize = 1048576; // 每一块数据的大小,可以不设置,默认为1M
  139. // 绑定每一块数据下载完成事件
  140. $download->on('progressChanged', function($e) {
  141. var_dump($e);
  142. });
  143. // 获取文件大小
  144. echo $download->getFileSize(), PHP_EOL;
  145. // 下载
  146. $download->download(__DIR__ . '/1.exe');
  147. // 获取是否使用断点续传分块下载
  148. var_dump($download->isBreakContinue);
  149. } catch (\Exception $e) {
  150. var_dump($e->getMessage());
  151. }