ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
PHPChunked.php
Go to the documentation of this file.
1<?php
2
4
8
9require_once('./Services/FileDelivery/interfaces/int.ilFileDeliveryType.php');
10
18final class PHPChunked implements ilFileDeliveryType
19{
20
24 private $httpService;
25
26
32 public function __construct(GlobalHttpState $httpState)
33 {
34 $this->httpService = $httpState;
35 }
36
37
41 public function doesFileExists($path_to_file)
42 {
43 return is_readable($path_to_file);
44 }
45
46
50 public function prepare($path_to_file)
51 {
52 return true;
53 }
54
55
59 public function deliver($path_to_file, $file_marked_to_delete)
60 {
61 $file = $path_to_file;
62 $fp = @fopen($file, 'rb');
63
64 $size = filesize($file); // File size
65 $length = $size; // Content length
66 $start = 0; // Start byte
67 $end = $size - 1; // End byte
68 // Now that we've gotten so far without errors we send the accept range header
69 /* At the moment we only support single ranges.
70 * Multiple ranges requires some more work to ensure it works correctly
71 * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
72 *
73 * Multirange support annouces itself with:
74 * header('Accept-Ranges: bytes');
75 *
76 * Multirange content must be sent with multipart/byteranges mediatype,
77 * (mediatype = mimetype)
78 * as well as a boundry header to indicate the various chunks of data.
79 */
80 $response = $this->httpService->response()->withHeader("Accept-Ranges", "0-$length");
81 $this->httpService->saveResponse($response);
82 $server = $this->httpService->request()->getServerParams();
83 // header('Accept-Ranges: bytes');
84 // multipart/byteranges
85 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
86 if (isset($server['HTTP_RANGE'])) {
87 $c_start = $start;
88 $c_end = $end;
89
90 // Extract the range string
91 list(, $range) = explode('=', $server['HTTP_RANGE'], 2);
92 // Make sure the client hasn't sent us a multibyte range
93 if (strpos($range, ',') !== false) {
94 // (?) Shoud this be issued here, or should the first
95 // range be used? Or should the header be ignored and
96 // we output the whole content?
97 $response = $this->httpService->response()->withStatus(416)->withHeader(ResponseHeader::CONTENT_RANGE, "bytes $start-$end/$size");
98 $this->httpService->saveResponse($response);
99
100 //header("Content-Range: bytes $start-$end/$size");
101 // (?) Echo some info to the client?
102 $this->close();
103 } // fim do if
104 // If the range starts with an '-' we start from the beginning
105 // If not, we forward the file pointer
106 // And make sure to get the end byte if spesified
107 if ($range{0} == '-') {
108 // The n-number of the last bytes is requested
109 $c_start = $size - substr($range, 1);
110 } else {
111 $range = explode('-', $range);
112 $c_start = $range[0];
113 $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
114 } // fim do if
115 /* Check the range and make sure it's treated according to the specs.
116 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
117 */
118 // End bytes can not be larger than $end.
119 $c_end = ($c_end > $end) ? $end : $c_end;
120 // Validate the requested range and return an error if it's not correct.
121 if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
122 $response = $this->httpService->response()->withStatus(416)->withHeader(ResponseHeader::CONTENT_RANGE, "bytes $start-$end/$size");
123
124 $this->httpService->saveResponse($response);
125 // (?) Echo some info to the client?
126 $this->close();
127 } // fim do if
128
129 $start = $c_start;
130 $end = $c_end;
131 $length = $end - $start + 1; // Calculate new content length
132 fseek($fp, $start);
133
134 $response = $this->httpService->response()->withStatus(206);
135
136 $this->httpService->saveResponse($response);
137 } // fim do if
138
139 // Notify the client the byte range we'll be outputting
140 $response = $this->httpService->response()->withHeader(ResponseHeader::CONTENT_RANGE, "bytes $start-$end/$size")->withHeader(ResponseHeader::CONTENT_LENGTH, $length);
141
142 $this->httpService->saveResponse($response);
143
144 //render response and start buffered download
145 $this->httpService->sendResponse();
146
147 // Start buffered download
148 $buffer = 1024 * 8;
149 while (!feof($fp) && ($p = ftell($fp)) <= $end) {
150 if ($p + $buffer > $end) {
151 // In case we're only outputtin a chunk, make sure we don't
152 // read past the length
153 $buffer = $end - $p + 1;
154 } // fim do if
155
156 set_time_limit(0); // Reset time limit for big files
157 echo fread($fp, $buffer);
158 flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
159 } // fim do while
160
161 fclose($fp);
162
163 return true;
164 }
165
166
170 public function supportsInlineDelivery()
171 {
172 return true;
173 }
174
175
180 {
181 return true;
182 }
183
184
188 public function supportsStreaming()
189 {
190 return true;
191 }
192
193
194 private function close()
195 {
196 //render response
197 $this->httpService->sendResponse();
198 exit;
199 }
200
201
205 public function handleFileDeletion($path_to_file)
206 {
207 return unlink($path_to_file);
208 }
209}
$size
Definition: RandomTest.php:84
An exception for terminatinating execution or to throw for unit testing.
deliver($path_to_file, $file_marked_to_delete)
bool
Definition: PHPChunked.php:59
__construct(GlobalHttpState $httpState)
PHP constructor.
Definition: PHPChunked.php:32
$server
Definition: getUserInfo.php:12
Interface GlobalHttpState.
Interface ResponseHeader.
$end
Definition: saml1-acs.php:18
$response
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file