4 * BigGet - download big files efficiently
7 * @copyright Michael Wallner, <mike@iworks.at>
8 * @license BSD, revised
11 class BigGet
extends HttpRequestPool
19 * Parallel Request count
24 * Whether to output debug messages
45 * Size of requested resource
52 * Whether the requests have been sent
56 private $sent = false;
73 public static function url($url, $tmp = '/tmp')
75 $head = new HttpRequest($url, HttpRequest
::METH_HEAD
);
76 $headers = $head->send()->getHeaders();
78 if (200 != $head->getResponseCode()) {
79 throw new HttpException("Did not receive '200 Ok' from HEAD $url");
81 if (!isset($headers['Accept-Ranges'])) {
82 throw new HttpException("Did not receive an Accept-Ranges header from HEAD $url");
84 if (!isset($headers['Content-Length'])) {
85 throw new HttpException("Did not receive a Content-Length header from HEAD $url");
90 $bigget->tmp
= tempnam($tmp, 'BigGet.');
91 $bigget->size
= $headers['Content-Length'];
96 * Save the resource to a file
102 public function saveTo($file)
104 $this->sent
or $this->send();
106 if ($w = fopen($this->tmp
, 'wb')) {
108 $this->dbg
&& print "\nCopying temp files to $file ...\n";
110 foreach (glob($this->tmp
.".????") as $tmp) {
112 $this->dbg
&& print "\t$tmp\n";
114 if ($r = fopen($tmp, 'rb')) {
115 stream_copy_to_stream($r, $w);
121 rename($this->tmp
, $file);
123 $this->dbg
&& print "\nDone.\n";
131 * Overrides HttpRequestPool::send()
136 public function send()
140 // use max RMAX simultanous requests with a req size of SIZE
141 while ($this->count
< self
::RMAX
&& -1 != $offset = $this->getRangeOffset()) {
142 $this->attachNew($offset);
145 while ($this->socketPerform()) {
146 if (!$this->socketSelect()) {
147 throw new HttpSocketException
;
153 * Overrides HttpRequestPool::socketPerform()
157 protected function socketPerform()
160 $rs = parent
::socketPerform();
161 } catch (HttpRequestPoolException
$x) {
162 foreach ($x->exceptionStack
as $e) {
163 echo $e->getMessage(), "\n";
167 foreach ($this->getFinishedRequests() as $r) {
170 if (206 != $rc = $r->getResponseCode()) {
171 throw new HttpException("Unexpected response code: $rc");
174 file_put_contents(sprintf("%s.%04d", $this->tmp
, $r->id
), $r->getResponseBody());
176 if (-1 != $offset = $this->getRangeOffset()) {
177 $this->attachNew($offset);
184 private function attachNew($offset)
186 $stop = min($this->count
* self
::SIZE + self
::SIZE
, $this->size
) - 1;
188 $this->dbg
&& print "Attaching new request to get range: $offset-$stop\n";
190 $req = new BigGetRequest(
192 HttpRequest
::METH_GET
,
195 'Range' => "bytes=$offset-$stop"
200 $req->id
= $this->count++
;
203 private function getRangeOffset()
205 return ($this->size
>= $start = $this->count
* self
::SIZE
) ?
$start : -1;
214 class BigGetRequest
extends HttpRequest