ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
PHPChunked.php
Go to the documentation of this file.
1 <?php
2 
4 
8 
9 require_once('./Services/FileDelivery/interfaces/int.ilFileDeliveryType.php');
10 
18 final 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 
179  public function supportsAttachmentDelivery()
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 }
Interface GlobalHttpState.
$size
Definition: RandomTest.php:84
__construct(GlobalHttpState $httpState)
PHP constructor.
Definition: PHPChunked.php:32
$server
Definition: sabredav.php:48
$start
Definition: bench.php:8
exit
Definition: backend.php:16
deliver($path_to_file, $file_marked_to_delete)
absolute path to fileThis is needed at this point for header-based delivery methodsbool ...
Definition: PHPChunked.php:59
$response